From d1aa8f01e70e78d7ae438059872d4f7ef52bff1a Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 25 Feb 2026 18:21:35 +1300 Subject: [PATCH] Change sticker URL and stream response --- src/m2d/actions/sticker.js | 33 ++++++++------------------ src/m2d/converters/event-to-message.js | 12 +++------- src/web/routes/download-matrix.js | 21 ++++++++-------- 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/src/m2d/actions/sticker.js b/src/m2d/actions/sticker.js index f9c06bf..341d8b0 100644 --- a/src/m2d/actions/sticker.js +++ b/src/m2d/actions/sticker.js @@ -1,7 +1,7 @@ // @ts-check -const streamr = require("stream") -const {pipeline} = require("stream").promises +const {Readable} = require("stream") +const {ReadableStream} = require("stream/web") const {sync} = require("../../passthrough") const sharp = require("sharp") @@ -16,7 +16,7 @@ const HEIGHT = 160 /** * Downloads the sticker from the web and converts to webp data. * @param {string} mxc a single mxc:// URL - * @returns {Promise} sticker webp data, or undefined if the downloaded sticker is not valid + * @returns {Promise} sticker webp data, or undefined if the downloaded sticker is not valid */ async function getAndResizeSticker(mxc) { const res = await api.getMedia(mxc) @@ -24,29 +24,16 @@ async function getAndResizeSticker(mxc) { const root = await res.json() throw new mreq.MatrixServerError(root, {mxc}) } - const streamIn = streamr.Readable.fromWeb(res.body) + const streamIn = Readable.fromWeb(res.body) const { stream, mime } = await streamMimeType.getMimeType(streamIn) - let animated = false - if (mime === "image/gif" || mime === "image/webp") { - animated = true - } + const animated = ["image/gif", "image/webp"].includes(mime) - const result = await new Promise((resolve, reject) => { - const transformer = sharp({animated: animated}) - .resize(WIDTH, HEIGHT, {fit: "inside", background: {r: 0, g: 0, b: 0, alpha: 0}}) - .webp() - .toBuffer((err, buffer, info) => { - /* c8 ignore next */ - if (err) return reject(err) - resolve({info, buffer}) - }) - pipeline( - stream, - transformer - ) - }) - return result.buffer + const transformer = sharp({animated: animated}) + .resize(WIDTH, HEIGHT, {fit: "inside", background: {r: 0, g: 0, b: 0, alpha: 0}}) + .webp() + stream.pipe(transformer) + return Readable.toWeb(transformer) } diff --git a/src/m2d/converters/event-to-message.js b/src/m2d/converters/event-to-message.js index 91c2400..81ad48c 100644 --- a/src/m2d/converters/event-to-message.js +++ b/src/m2d/converters/event-to-message.js @@ -631,17 +631,11 @@ async function eventToMessage(event, guild, channel, di) { } if (event.type === "m.sticker") { - content = "" - content += `[${event.content.body}](` // sticker title for fallback if the url preview fails - const afterLink = ")" - - // Make sticker URL params - const params = new URLSearchParams() const withoutMxc = mxUtils.makeMxcPublic(event.content.url) assert(withoutMxc) - params.append("mxc", withoutMxc) - const url = `${reg.ooye.bridge_origin}/download/sticker.webp?${params.toString()}` - content += url + afterLink + const url = `${reg.ooye.bridge_origin}/download/sticker/${withoutMxc}/_.webp` + content = `[${event.content.body || "\u2800"}](${url})` + } else if (event.type === "org.matrix.msc3381.poll.start") { const pollContent = event.content["org.matrix.msc3381.poll.start"] // just for convenience const isClosed = false; diff --git a/src/web/routes/download-matrix.js b/src/web/routes/download-matrix.js index 1fbc1d6..82e2f7e 100644 --- a/src/web/routes/download-matrix.js +++ b/src/web/routes/download-matrix.js @@ -28,7 +28,8 @@ const schema = { e: z.array(z.string()).or(z.string()) }), sticker: z.object({ - mxc: z.string() + server_name: z.string().regex(/^[^/]+$/), + media_id: z.string().regex(/^[A-Za-z0-9_-]+$/) }) } @@ -97,15 +98,13 @@ as.router.get(`/download/sheet`, defineEventHandler(async event => { return buffer })) -as.router.get(`/download/sticker.webp`, defineEventHandler(async event => { - const query = await getValidatedQuery(event, schema.sticker.parse) +as.router.get(`/download/sticker/:server_name/:media_id/_.webp`, defineEventHandler(async event => { + const {server_name, media_id} = await getValidatedRouterParams(event, schema.sticker.parse) + /** remember that this has no mxc:// protocol in the string */ + const mxc = server_name + "/" + media_id + verifyMediaHash(mxc) - /** remember that these have no mxc:// protocol in the string */ - verifyMediaHash(query.mxc) - const mxc = `mxc://${query.mxc}` - - setResponseHeader(event, "Content-Type", 'image/webp') - const buffer = await sticker.getAndResizeSticker(mxc) - return buffer + const stream = await sticker.getAndResizeSticker(`mxc://${mxc}`) + setResponseHeader(event, "Content-Type", "image/webp") + return stream })) -