diff --git a/.gitignore b/.gitignore index 9a310ad..ebfcc07 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ config.js registration.yaml coverage db/ooye.db* +test/res/butterfly* diff --git a/m2d/converters/emoji-sheet.test.js b/m2d/converters/emoji-sheet.test.js index de3a2ab..026c53f 100644 --- a/m2d/converters/emoji-sheet.test.js +++ b/m2d/converters/emoji-sheet.test.js @@ -1,7 +1,7 @@ const assert = require("assert").strict const {test} = require("supertape") const {_convertImageStream} = require("./emoji-sheet") -const fetch = require("node-fetch") +const fs = require("fs") const {Transform} = require("stream").Transform /* c8 ignore next 7 */ @@ -25,18 +25,16 @@ class Meter extends Transform { /** * @param {import("supertape").Test} t - * @param {string} url + * @param {string} path * @param {number} totalSize */ -async function runSingleTest(t, url, totalSize) { - const abortController = new AbortController() - const res = await fetch("https://ezgif.com/images/format-demo/butterfly.png", {agent: false, signal: abortController.signal}) +async function runSingleTest(t, path, totalSize) { + const file = fs.createReadStream(path) const meter = new Meter() - const p = res.body.pipe(meter) + const p = file.pipe(meter) const result = await _convertImageStream(p, () => { - abortController.abort() - res.body.pause() - res.body.emit("end") + file.pause() + file.emit("end") }) t.equal(result.subarray(1, 4).toString("ascii"), "PNG", `result was not a PNG file: ${result.toString("base64")}`) /* c8 ignore next 5 */ @@ -48,9 +46,9 @@ async function runSingleTest(t, url, totalSize) { } slow()("emoji-sheet: only partial file is read for APNG", async t => { - await runSingleTest(t, "https://ezgif.com/images/format-demo/butterfly.png", 2438998) + await runSingleTest(t, "test/res/butterfly.png", 2438998) }) slow()("emoji-sheet: only partial file is read for GIF", async t => { - await runSingleTest(t, "https://ezgif.com/images/format-demo/butterfly.gif", 781223) + await runSingleTest(t, "test/res/butterfly.gif", 781223) }) diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index 8710531..4ffe26e 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -209,14 +209,19 @@ function convertEmoji(mxcUrl, nameForGuess, allowSpriteSheetIndicator, allowLink // Get the known emoji from the database. let row if (mxcUrl) row = select("emoji", ["emoji_id", "name", "animated"], {mxc_url: mxcUrl}).get() + // Now we have to search all servers to see if we're able to send this emoji. + if (row) { + const found = [...discord.guilds.values()].find(g => g.emojis.find(e => e.id === row.id)) + if (!found) row = null + } + // Or, if we don't have an emoji right now, we search for the name instead. if (!row && nameForGuess) { - // We don't know the emoji, but we could guess a suitable emoji based on the name const nameForGuessLower = nameForGuess.toLowerCase() for (const guild of discord.guilds.values()) { /** @type {{name: string, id: string, animated: number}[]} */ // @ts-ignore const emojis = guild.emojis - const found = emojis.find(e => e.name?.toLowerCase() === nameForGuessLower) + const found = emojis.find(e => e.id === row?.id || e.name?.toLowerCase() === nameForGuessLower) if (found) { row = { animated: found.animated, @@ -643,19 +648,6 @@ 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]) - assert(typeof match.index === "number", "Your JavaScript implementation does not comply with TC39: https://tc39.es/ecma262/multipage/text-processing.html#sec-regexpbuiltinexec") - last = match.index - } - // Handling written @mentions: we need to look for candidate Discord members to join to the room // This shouldn't apply to code blocks, links, or inside attributes. So editing the HTML tree instead of regular expressions is a sensible choice here. // We're using the domino parser because Turndown uses the same and can reuse this tree. @@ -701,6 +693,19 @@ async function eventToMessage(event, guild, di) { } await forEachNode(root) + // 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]) + assert(typeof match.index === "number", "Your JavaScript implementation does not comply with TC39: https://tc39.es/ecma262/multipage/text-processing.html#sec-regexpbuiltinexec") + last = match.index + } + // @ts-ignore bad type from turndown content = turndownService.turndown(root) diff --git a/package.json b/package.json index 658fa3a..3ecb7a8 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "scripts": { "addbot": "node addbot.js", "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", + "test-slow": "cross-env FORCE_COLOR=true SUPERTAPE_TIMEOUT=6000 supertape --no-check-assertions-count --format tap test/test.js -- --slow | tap-dot", "cover": "c8 --skip-full -x db/migrations -x matrix/file.js -x matrix/api.js -x matrix/mreq.js -r html -r text supertape --no-check-assertions-count --format fail test/test.js -- --slow" } } diff --git a/test/test.js b/test/test.js index 263ccc7..3e5ba72 100644 --- a/test/test.js +++ b/test/test.js @@ -7,6 +7,9 @@ const migrate = require("../db/migrate") const HeatSync = require("heatsync") const {test} = require("supertape") const data = require("./data") +/** @type {import("node-fetch").default} */ +// @ts-ignore +const fetch = require("node-fetch") const config = require("../config") const passthrough = require("../passthrough") @@ -60,6 +63,27 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not }) db.exec(fs.readFileSync(join(__dirname, "ooye-test-data.sql"), "utf8")) + + /* c8 ignore start - maybe download some more test files in slow mode */ + if (process.argv.includes("--slow")) { + test("test files: download", async t => { + function download(url, to) { + return new Promise(async resolve => { + if (fs.existsSync(to)) return resolve(null) + const res = await fetch(url) + res.body.pipe(fs.createWriteStream(to, {encoding: "binary"})) + res.body.once("finish", resolve) + }) + } + await Promise.all([ + download("https://ezgif.com/images/format-demo/butterfly.png", "test/res/butterfly.png"), + download("https://ezgif.com/images/format-demo/butterfly.gif", "test/res/butterfly.gif") + ]) + t.pass("downloaded") + }) + } + /* c8 ignore end */ + require("../db/orm.test") require("../discord/utils.test") require("../matrix/kstate.test")