forked from cadence/out-of-your-element
Pull from Cadence, to avoid future merge conflicts #2
3 changed files with 81 additions and 16 deletions
53
src/m2d/actions/sticker.js
Normal file
53
src/m2d/actions/sticker.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// @ts-check
|
||||
|
||||
const streamr = require("stream")
|
||||
const {pipeline} = require("stream").promises
|
||||
|
||||
const {sync} = require("../../passthrough")
|
||||
const sharp = require("sharp")
|
||||
/** @type {import("../../matrix/api")} */
|
||||
const api = sync.require("../../matrix/api")
|
||||
/** @type {import("../../matrix/mreq")} */
|
||||
const mreq = sync.require("../../matrix/mreq")
|
||||
const streamMimeType = require("stream-mime-type")
|
||||
|
||||
const WIDTH = 160
|
||||
const HEIGHT = 160
|
||||
/**
|
||||
* Downloads the sticker from the web and converts to webp data.
|
||||
* @param {string} mxc a single mxc:// URL
|
||||
* @returns {Promise<Buffer | undefined>} sticker webp data, or undefined if the downloaded sticker is not valid
|
||||
*/
|
||||
async function getAndResizeSticker(mxc) {
|
||||
const res = await api.getMedia(mxc)
|
||||
if (res.status !== 200) {
|
||||
const root = await res.json()
|
||||
throw new mreq.MatrixServerError(root, {mxc})
|
||||
}
|
||||
const streamIn = streamr.Readable.fromWeb(res.body)
|
||||
|
||||
const { stream, mime } = await streamMimeType.getMimeType(streamIn)
|
||||
let animated = false
|
||||
if (mime === "image/gif" || mime === "image/webp") {
|
||||
animated = true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
module.exports.getAndResizeSticker = getAndResizeSticker
|
||||
|
|
@ -632,23 +632,16 @@ async function eventToMessage(event, guild, channel, di) {
|
|||
|
||||
if (event.type === "m.sticker") {
|
||||
content = ""
|
||||
let filename = event.content.body
|
||||
if (event.type === "m.sticker") {
|
||||
let mimetype
|
||||
if (event.content.info?.mimetype?.includes("/")) {
|
||||
mimetype = event.content.info.mimetype
|
||||
} else {
|
||||
const res = await di.api.getMedia(event.content.url, {method: "HEAD"})
|
||||
if (res.status === 200) {
|
||||
mimetype = res.headers.get("content-type")
|
||||
}
|
||||
if (!mimetype) throw new Error(`Server error ${res.status} or missing content-type while detecting sticker mimetype`)
|
||||
}
|
||||
filename += "." + mimetype.split("/")[1]
|
||||
}
|
||||
attachments.push({id: "0", filename})
|
||||
pendingFiles.push({name: filename, mxc: event.content.url})
|
||||
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
|
||||
} else if (event.type === "org.matrix.msc3381.poll.start") {
|
||||
const pollContent = event.content["org.matrix.msc3381.poll.start"] // just for convenience
|
||||
const isClosed = false;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ const emojiSheet = sync.require("../../m2d/actions/emoji-sheet")
|
|||
/** @type {import("../../m2d/converters/emoji-sheet")} */
|
||||
const emojiSheetConverter = sync.require("../../m2d/converters/emoji-sheet")
|
||||
|
||||
/** @type {import("../../m2d/actions/sticker")} */
|
||||
const sticker = sync.require("../../m2d/actions/sticker")
|
||||
|
||||
const schema = {
|
||||
params: z.object({
|
||||
server_name: z.string(),
|
||||
|
|
@ -23,6 +26,9 @@ const schema = {
|
|||
}),
|
||||
sheet: z.object({
|
||||
e: z.array(z.string()).or(z.string())
|
||||
}),
|
||||
sticker: z.object({
|
||||
mxc: z.string()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -90,3 +96,16 @@ as.router.get(`/download/sheet`, defineEventHandler(async event => {
|
|||
setResponseHeader(event, "Content-Type", "image/png")
|
||||
return buffer
|
||||
}))
|
||||
|
||||
as.router.get(`/download/sticker.webp`, defineEventHandler(async event => {
|
||||
const query = await getValidatedQuery(event, schema.sticker.parse)
|
||||
|
||||
/** 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
|
||||
}))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue