diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index 58488e7..671940f 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -122,7 +122,7 @@ async function channelToKState(channel, guild) { network: { id: guild.id, displayname: guild.name, - avatar_url: await file.uploadDiscordFileToMxc(file.guildIcon(guild)) + avatar_url: file.DISCORD_IMAGES_BASE + file.guildIcon(guild) }, channel: { id: channel.id, diff --git a/m2d/actions/send-event.js b/m2d/actions/send-event.js index ac4ef60..005b031 100644 --- a/m2d/actions/send-event.js +++ b/m2d/actions/send-event.js @@ -17,19 +17,13 @@ const eventToMessage = sync.require("../converters/event-to-message") const api = sync.require("../../matrix/api") /** - * @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer}[], pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]}} message + * @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string})[]}} message * @returns {Promise} */ async function resolvePendingFiles(message) { if (!message.pendingFiles) return message const files = await Promise.all(message.pendingFiles.map(async p => { let fileBuffer - if ("buffer" in p) { - return { - name: p.name, - file: p.buffer - } - } if ("key" in p) { // Encrypted const d = crypto.createDecipheriv("aes-256-ctr", Buffer.from(p.key, "base64url"), Buffer.from(p.iv, "base64url")) @@ -50,7 +44,7 @@ async function resolvePendingFiles(message) { })) const newMessage = { ...message, - files: files.concat(message.files || []) + files } delete newMessage.pendingFiles return newMessage diff --git a/m2d/converters/emoji-sheet.js b/m2d/converters/emoji-sheet.js deleted file mode 100644 index 7e698c5..0000000 --- a/m2d/converters/emoji-sheet.js +++ /dev/null @@ -1,95 +0,0 @@ -// @ts-check - -const assert = require("assert").strict -const {pipeline} = require("stream").promises -const sharp = require("sharp") -const {GIFrame} = require("giframe") -const utils = require("./utils") -const fetch = require("node-fetch").default -const streamMimeType = require("stream-mime-type") - -const SIZE = 48 -const RESULT_WIDTH = 400 -const IMAGES_ACROSS = Math.floor(RESULT_WIDTH / SIZE) - -/** - * Composite a bunch of Matrix emojis into a kind of spritesheet image to upload to Discord. - * @param {string[]} mxcs mxc URLs, in order - * @returns {Promise} PNG image - */ -async function compositeMatrixEmojis(mxcs) { - let buffers = await Promise.all(mxcs.map(async mxc => { - const abortController = new AbortController() - - try { - const url = utils.getPublicUrlForMxc(mxc) - assert(url) - - /** @type {import("node-fetch").Response} res */ - // If it turns out to be a GIF, we want to abandon the connection without downloading the whole thing. - // If we were using connection pooling, we would be forced to download the entire GIF. - // So we set no agent to ensure we are not connection pooling. - // @ts-ignore the signal is slightly different from the type it wants (still works fine) - const res = await fetch(url, {agent: false, signal: abortController.signal}) - const {stream, mime} = await streamMimeType.getMimeType(res.body) - - if (mime === "image/png" || mime === "image/jpeg" || mime === "image/webp") { - /** @type {{info: sharp.OutputInfo, buffer: Buffer}} */ - const result = await new Promise((resolve, reject) => { - const transformer = sharp() - .resize(SIZE, SIZE, {fit: "contain"}) - .png({compressionLevel: 0}) - .toBuffer((err, buffer, info) => { - if (err) return reject(err) - resolve({info, buffer}) - }) - pipeline( - stream, - transformer - ) - }) - return result.buffer - - } else if (mime === "image/gif") { - const giframe = new GIFrame(0) - stream.on("data", chunk => { - giframe.feed(chunk) - }) - const frame = await giframe.getFrame() - - const buffer = await sharp(frame.pixels, {raw: {width: frame.width, height: frame.height, channels: 4}}) - .resize(SIZE, SIZE, {fit: "contain", background: {r: 0, g: 0, b: 0, alpha: 0}}) - .png({compressionLevel: 0}) - .toBuffer({resolveWithObject: true}) - return buffer.data - - } else { - // unsupported mime type - console.error(`I don't know what a ${mime} emoji is.`) - return null - } - } finally { - abortController.abort() - } - })) - - const totalWidth = Math.min(buffers.length, IMAGES_ACROSS) * SIZE - const imagesDown = Math.ceil(buffers.length / IMAGES_ACROSS) - const totalHeight = imagesDown * SIZE - const comp = [] - let left = 0, top = 0 - for (const buffer of buffers) { - if (Buffer.isBuffer(buffer)) { - comp.push({left, top, input: buffer}) - ;(left += SIZE) + SIZE > RESULT_WIDTH && (left = 0, top += SIZE) - } - } - - const output = await sharp({create: {width: totalWidth, height: totalHeight, channels: 4, background: {r: 0, g: 0, b: 0, alpha: 0}}}) - .composite(comp) - .png() - .toBuffer({resolveWithObject: true}) - return output.data -} - -module.exports.compositeMatrixEmojis = compositeMatrixEmojis diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index b78574c..18dee17 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -12,8 +12,6 @@ const {sync, db, discord, select, from} = passthrough const file = sync.require("../../matrix/file") /** @type {import("../converters/utils")} */ const utils = sync.require("../converters/utils") -/** @type {import("./emoji-sheet")} */ -const emojiSheet = sync.require("./emoji-sheet") const BLOCK_ELEMENTS = [ "ADDRESS", "ARTICLE", "ASIDE", "AUDIO", "BLOCKQUOTE", "BODY", "CANVAS", @@ -119,21 +117,15 @@ turndownService.addRule("inlineLink", { } }) -/** @type {string[]} SPRITE SHEET EMOJIS FEATURE: mxc urls for the currently processing message */ -let endOfMessageEmojis = [] turndownService.addRule("emoji", { filter: function (node, options) { - if (node.nodeName !== "IMG" || !node.hasAttribute("data-mx-emoticon") || !node.getAttribute("src") || !node.getAttribute("title")) return false - return true - }, - - replacement: function (content, node) { - const mxcUrl = node.getAttribute("src") - let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl) + if (node.nodeName !== "IMG" || !node.hasAttribute("data-mx-emoticon") || !node.getAttribute("src")) return false + let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(node.getAttribute("src")) if (!row) { // We don't know what this is... but maybe we can guess based on the name? - const guessedName = node.getAttribute("title").replace(/^:|:$/g, "") - for (const guild of discord?.guilds.values() || []) { + const guessedName = node.getAttribute("title")?.replace?.(/^:|:$/g, "") + if (!guessedName) return false + for (const guild of discord.guilds.values()) { /** @type {{name: string, id: string, animated: number}[]} */ // @ts-ignore const emojis = guild.emojis @@ -144,18 +136,21 @@ turndownService.addRule("emoji", { } } } - if (row) { - const animatedChar = row.animated ? "a" : "" - return `<${animatedChar}:${row.name}:${row.id}>` - } else { - if (endOfMessageEmojis.includes(mxcUrl)) { - // After control returns to the main converter, it will rewind over this, delete this section, and upload the emojis as a sprite sheet. - return `<::>` - } else { - // This emoji is not at the end of the message, it is in the middle. We don't upload middle emojis as a sprite sheet. - return `[${node.getAttribute("title")}](${utils.getPublicUrlForMxc(mxcUrl)})` - } - } + if (!row) return false + node.setAttribute("data-emoji-id", row.id) + node.setAttribute("data-emoji-name", row.name) + node.setAttribute("data-emoji-animated-char", row.animated ? "a" : "") + return true + }, + + replacement: function (content, node) { + /** @type {string} */ + const id = node.getAttribute("data-emoji-id") + /** @type {string} */ + const animatedChar = node.getAttribute("data-emoji-animated-char") + /** @type {string} */ + const name = node.getAttribute("data-emoji-name") + return `<${animatedChar}:${name}:${id}>` } }) @@ -225,29 +220,6 @@ function splitDisplayName(displayName) { } } -/** - * At the time of this executing, we know what the end of message emojis are, and we know that at least one of them is unknown. - * This function will strip them from the content and generate the correct pending file of the sprite sheet. - * @param {string} content - * @param {{id: string, name: string}[]} attachments - * @param {({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]} pendingFiles - */ -async function uploadEndOfMessageSpriteSheet(content, attachments, pendingFiles) { - if (!content.includes("<::>")) return content // No unknown emojis, nothing to do - // Remove known and unknown emojis from the end of the message - const r = /\s*$/ - while (content.match(r)) { - content = content.replace(r, "") - } - // Create a sprite sheet of known and unknown emojis from the end of the message - const buffer = await emojiSheet.compositeMatrixEmojis(endOfMessageEmojis) - // Attach it - const name = "emojis.png" - attachments.push({id: "0", name}) - pendingFiles.push({name, buffer}) - return content -} - /** * @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker | Ty.Event.Outer_M_Room_Message_Encrypted_File} event * @param {import("discord-api-types/v10").APIGuild} guild @@ -279,7 +251,7 @@ async function eventToMessage(event, guild, di) { let content = event.content.body // ultimate fallback const attachments = [] - /** @type {({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]} */ + /** @type {({name: string, url: string} | {name: string, url: string, key: string, iv: string})[]} */ const pendingFiles = [] // Convert content depending on what the message is @@ -415,27 +387,11 @@ async function eventToMessage(event, guild, di) { // input = input.replace(/ /g, " ") // There is also a corresponding test to uncomment, named "event2message: whitespace is retained" - // SPRITE SHEET EMOJIS FEATURE: Emojis at the end of the message that we don't know about will be reuploaded as a sprite sheet. - // First we need to determine which emojis are at the end. - endOfMessageEmojis = [] - let match - let last = input.length - while ((match = input.slice(0, last).match(/]*>\s*$/))) { - if (!match[0].includes("data-mx-emoticon")) break - const mxcUrl = match[0].match(/\bsrc="(mxc:\/\/[^"]+)"/) - if (mxcUrl) endOfMessageEmojis.unshift(mxcUrl[1]) - if (typeof match.index !== "number") break - last = match.index - } - // @ts-ignore bad type from turndown content = turndownService.turndown(input) // It's designed for commonmark, we need to replace the space-space-newline with just newline content = content.replace(/ \n/g, "\n") - - // SPRITE SHEET EMOJIS FEATURE: - content = await uploadEndOfMessageSpriteSheet(content, attachments, pendingFiles) } else { // Looks like we're using the plaintext body! content = event.content.body diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js index a5cbe4d..3ec8dac 100644 --- a/m2d/converters/event-to-message.test.js +++ b/m2d/converters/event-to-message.test.js @@ -4,14 +4,6 @@ const data = require("../../test/data") const {MatrixServerError} = require("../../matrix/mreq") const {db, select} = require("../../passthrough") -function slow() { - if (process.argv.includes("--slow")) { - return test - } else { - return test.skip - } -} - /** * @param {string} roomID * @param {string} eventID @@ -1752,81 +1744,3 @@ test("event2message: animated emojis work", async t => { } ) }) - -test("event2message: unknown emojis in the middle are linked", async t => { - t.deepEqual( - await eventToMessage({ - type: "m.room.message", - sender: "@cadence:cadence.moe", - content: { - msgtype: "m.text", - body: "wrong body", - format: "org.matrix.custom.html", - formatted_body: 'a \":ms_robot_grin:\" b' - }, - event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", - room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe" - }), - { - messagesToDelete: [], - messagesToEdit: [], - messagesToSend: [{ - username: "cadence [they]", - content: "a [:ms_robot_grin:](https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/RLMgJGfgTPjIQtvvWZsYjhjy) b", - avatar_url: undefined - }] - } - ) -}) - -slow()("event2message: unknown emoji in the end is reuploaded as a sprite sheet", async t => { - const messages = await eventToMessage({ - type: "m.room.message", - sender: "@cadence:cadence.moe", - content: { - msgtype: "m.text", - body: "wrong body", - format: "org.matrix.custom.html", - formatted_body: 'a b \":ms_robot_grin:\"' - }, - event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", - room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe" - }) - const testResult = { - content: messages.messagesToSend[0].content, - fileName: messages.messagesToSend[0].pendingFiles[0].name, - fileContentStart: messages.messagesToSend[0].pendingFiles[0].buffer.subarray(0, 90).toString("base64") - } - t.deepEqual(testResult, { - content: "a b", - fileName: "emojis.png", - fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAPoAAAD6AG1e1JrAAALkklEQVR4nM1ZeWyUxxV/azAGwn0JMJUppPhce++1Oc1i" - }) -}) - -slow()("event2message: known and unknown emojis in the end are reuploaded as a sprite sheet", async t => { - t.comment("SKIPPED") - const messages = await eventToMessage({ - type: "m.room.message", - sender: "@cadence:cadence.moe", - content: { - msgtype: "m.text", - body: "wrong body", - format: "org.matrix.custom.html", - formatted_body: 'known unknown: \":hippo:\" \":ms_robot_dress:\" and known unknown: \":hipposcope:\" \":ms_robot_cat:\"' - }, - event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", - room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe" - }) - const testResult = { - content: messages.messagesToSend[0].content, - fileName: messages.messagesToSend[0].pendingFiles[0].name, - fileContentStart: messages.messagesToSend[0].pendingFiles[0].buffer.subarray(0, 90).toString("base64") - } - require("fs").writeFileSync("/tmp/emojis.png", messages.messagesToSend[0].pendingFiles[0].buffer) - t.deepEqual(testResult, { - content: "known unknown: <:hippo:230201364309868544> [:ms_robot_dress:](https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/wcouHVjbKJJYajkhJLsyeJAA) and known unknown:", - fileName: "emojis.png", - fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAwCAYAAADuFn/PAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAT5UlEQVR4nOVbCXSVRZauR9gMsoYlvKwvARKSkPUlJOyL" - }) -}) diff --git a/package-lock.json b/package-lock.json index 15b4b76..6071d1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "better-sqlite3": "^8.3.0", "chunk-text": "^2.0.1", "cloudstorm": "^0.8.0", - "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3", - "giframe": "github:cloudrac3r/giframe#v0.4.1", + "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182", + "giframe": "github:cloudrac3r/giframe#v0.3.0", "heatsync": "^2.4.1", "js-yaml": "^4.1.0", "matrix-appservice": "^2.0.0", @@ -22,9 +22,7 @@ "node-fetch": "^2.6.7", "pngjs": "^7.0.0", "prettier-bytes": "^1.0.4", - "sharp": "^0.32.6", "snowtransfer": "^0.8.0", - "stream-mime-type": "^1.0.2", "try-to-catch": "^3.0.1", "turndown": "^7.1.2", "xxhash-wasm": "^1.0.2" @@ -311,11 +309,6 @@ "node": ">=16" } }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -458,11 +451,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==" - }, "node_modules/backtracker": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/backtracker/-/backtracker-3.3.2.tgz", @@ -792,22 +780,11 @@ "node": ">=12.0.0" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -818,16 +795,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colorette": { "version": "1.4.0", @@ -1034,9 +1003,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", "engines": { "node": ">=8" } @@ -1057,7 +1026,7 @@ }, "node_modules/discord-markdown": { "version": "2.4.1", - "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3", + "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182", "license": "MIT", "dependencies": { "simple-markdown": "^0.7.2" @@ -1241,11 +1210,6 @@ "node": ">= 0.8" } }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" - }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -1255,22 +1219,6 @@ "node": ">= 4.9.1" } }, - "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1429,7 +1377,7 @@ }, "node_modules/giframe": { "version": "0.3.0", - "resolved": "git+ssh://git@github.com/cloudrac3r/giframe.git#1630f4d3b2bf5acd197409c85edd11e0da72d0a1", + "resolved": "git+ssh://git@github.com/cloudrac3r/giframe.git#abbadea54772051a1e08f68c5046fb8f94329e44", "license": "MIT" }, "node_modules/github-from-package": { @@ -1666,11 +1614,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -2173,11 +2116,6 @@ "node": ">=10" } }, - "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" - }, "node_modules/node-fetch": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", @@ -2351,18 +2289,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/pngjs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", @@ -2477,11 +2403,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2546,34 +2467,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", @@ -2767,48 +2660,6 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/sharp": { - "version": "0.32.6", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", - "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "hasInstallScript": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.2", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.1", - "semver": "^7.5.4", - "simple-get": "^4.0.1", - "tar-fs": "^3.0.4", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", - "dependencies": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "node_modules/sharp/node_modules/tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2900,14 +2751,6 @@ "@types/react": ">=16.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/snowtransfer": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.8.3.tgz", @@ -2985,30 +2828,6 @@ "node": ">= 0.4" } }, - "node_modules/stream-head": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-head/-/stream-head-2.0.2.tgz", - "integrity": "sha512-aRkUMcmgbDl2Yjd5LqsB1LKB58Ot3JZ4ffuFMkFuvkPQT5X5XFMr4YK2dctApc+d3o52CXU1KUFisYaF/4zjAQ==", - "dependencies": { - "through2": "4.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stream-mime-type": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-mime-type/-/stream-mime-type-1.0.2.tgz", - "integrity": "sha512-80GzRn7JICPDEPBhSyqJjbztqX66+3DpkuUUcgDHtRBQlZRTkbCz0BsISggUl7AnyinJk9zyHVnd2lftlZXDdg==", - "dependencies": { - "file-type": "^16.0.1", - "mime-types": "^2.1.27", - "stream-head": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -3017,15 +2836,6 @@ "node": ">=10.0.0" } }, - "node_modules/streamx": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz", - "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==", - "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3092,22 +2902,6 @@ "node": ">=0.10.0" } }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/supertape": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/supertape/-/supertape-8.3.0.tgz", @@ -3302,27 +3096,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3331,22 +3104,6 @@ "node": ">=0.6" } }, - "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/package.json b/package.json index ad89303..5a561f0 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "chunk-text": "^2.0.1", "cloudstorm": "^0.8.0", "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3", - "giframe": "github:cloudrac3r/giframe#v0.4.1", + "giframe": "github:cloudrac3r/giframe#v0.3.0", "heatsync": "^2.4.1", "js-yaml": "^4.1.0", "matrix-appservice": "^2.0.0", @@ -28,9 +28,7 @@ "node-fetch": "^2.6.7", "pngjs": "^7.0.0", "prettier-bytes": "^1.0.4", - "sharp": "^0.32.6", "snowtransfer": "^0.8.0", - "stream-mime-type": "^1.0.2", "try-to-catch": "^3.0.1", "turndown": "^7.1.2", "xxhash-wasm": "^1.0.2" @@ -46,7 +44,6 @@ }, "scripts": { "test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot", - "test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js -- --slow | tap-dot", "cover": "c8 --skip-full -r html -r text supertape --no-check-assertions-count --format fail test/test.js" } } diff --git a/readme.md b/readme.md index 4a79d57..aac392b 100644 --- a/readme.md +++ b/readme.md @@ -139,6 +139,5 @@ I recommend developing in Visual Studio Code so that the JSDoc x TypeScript anno * (3) node-fetch@2: I like it and it does what I want. * (0) pngjs: Lottie stickers are converted to bitmaps with the vendored Rlottie WASM build, then the bitmaps are converted to PNG with pngjs. * (0) prettier-bytes: It does what I want and has no dependencies. -* (51) sharp: Jimp has fewer dependencies, but sharp is faster. * (0) try-to-catch: Not strictly necessary, but it does what I want and has no dependencies. * (1) turndown: I need an HTML-to-Markdown converter and this one looked suitable enough. It has some bugs that I've worked around, so I might switch away from it later. diff --git a/test/data.js b/test/data.js index a0e59d3..d6e10ef 100644 --- a/test/data.js +++ b/test/data.js @@ -46,24 +46,7 @@ module.exports = { "m.room.avatar": 0 } }, - "chat.schildi.hide_ui/read_receipts": {hidden: true}, - "uk.half-shot.bridge/moe.cadence.ooye://discord/112760669178241024/112760669178241024": { - bridgebot: "@_ooye_bot:cadence.moe", - protocol: { - id: "discord", - displayname: "Discord" - }, - network: { - id: "112760669178241024", - displayname: "Psychonauts 3", - avatar_url: "mxc://cadence.moe/zKXGZhmImMHuGQZWJEFKJbsF" - }, - channel: { - id: "112760669178241024", - displayname: "collective-unconscious", - external_url: "https://discord.com/channels/112760669178241024/112760669178241024" - } - } + "chat.schildi.hide_ui/read_receipts": {hidden: true} } }, guild: {