forked from cadence/out-of-your-element
Pull from Cadence, to avoid future merge conflicts #2
4 changed files with 72 additions and 21 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
const Ty = require("../src/types")
|
||||||
const assert = require("assert").strict
|
const assert = require("assert").strict
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const sqlite = require("better-sqlite3")
|
const sqlite = require("better-sqlite3")
|
||||||
|
|
@ -285,8 +286,8 @@ function defineEchoHandler() {
|
||||||
console.log()
|
console.log()
|
||||||
|
|
||||||
// Done with user prompts, reg is now guaranteed to be valid
|
// Done with user prompts, reg is now guaranteed to be valid
|
||||||
|
const mreq = require("../src/matrix/mreq")
|
||||||
const api = require("../src/matrix/api")
|
const api = require("../src/matrix/api")
|
||||||
const file = require("../src/matrix/file")
|
|
||||||
const DiscordClient = require("../src/d2m/discord-client")
|
const DiscordClient = require("../src/d2m/discord-client")
|
||||||
const discord = new DiscordClient(reg.ooye.discord_token, "no")
|
const discord = new DiscordClient(reg.ooye.discord_token, "no")
|
||||||
passthrough.discord = discord
|
passthrough.discord = discord
|
||||||
|
|
@ -343,7 +344,13 @@ function defineEchoHandler() {
|
||||||
await api.register(reg.sender_localpart)
|
await api.register(reg.sender_localpart)
|
||||||
|
|
||||||
// upload initial images...
|
// upload initial images...
|
||||||
const avatarUrl = await file.uploadDiscordFileToMxc("https://cadence.moe/friends/out_of_your_element.png")
|
const avatarBuffer = await fs.promises.readFile(join(__dirname, "..", "docs", "img", "icon.png"), null)
|
||||||
|
/** @type {Ty.R.FileUploaded} */
|
||||||
|
const root = await mreq.mreq("POST", "/media/v3/upload", avatarBuffer, {
|
||||||
|
headers: {"Content-Type": "image/png"}
|
||||||
|
})
|
||||||
|
const avatarUrl = root.content_uri
|
||||||
|
assert(avatarUrl)
|
||||||
|
|
||||||
console.log("✅ Matrix appservice login works...")
|
console.log("✅ Matrix appservice login works...")
|
||||||
|
|
||||||
|
|
@ -352,8 +359,7 @@ function defineEchoHandler() {
|
||||||
console.log("✅ Emojis are ready...")
|
console.log("✅ Emojis are ready...")
|
||||||
|
|
||||||
// set profile data on discord...
|
// set profile data on discord...
|
||||||
const avatarImageBuffer = await fetch("https://cadence.moe/friends/out_of_your_element.png").then(res => res.arrayBuffer())
|
await discord.snow.user.updateSelf({avatar: "data:image/png;base64," + avatarBuffer.toString("base64")})
|
||||||
await discord.snow.user.updateSelf({avatar: "data:image/png;base64," + Buffer.from(avatarImageBuffer).toString("base64")})
|
|
||||||
console.log("✅ Discord profile updated...")
|
console.log("✅ Discord profile updated...")
|
||||||
|
|
||||||
// set profile data on homeserver...
|
// set profile data on homeserver...
|
||||||
|
|
|
||||||
40
src/m2d/actions/sticker.js
Normal file
40
src/m2d/actions/sticker.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
const {Readable} = require("stream")
|
||||||
|
const {ReadableStream} = require("stream/web")
|
||||||
|
|
||||||
|
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<ReadableStream>} 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 = Readable.fromWeb(res.body)
|
||||||
|
const { stream, mime } = await streamMimeType.getMimeType(streamIn)
|
||||||
|
const animated = ["image/gif", "image/webp"].includes(mime)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports.getAndResizeSticker = getAndResizeSticker
|
||||||
|
|
@ -631,23 +631,10 @@ async function eventToMessage(event, guild, channel, di) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "m.sticker") {
|
if (event.type === "m.sticker") {
|
||||||
content = ""
|
const withoutMxc = mxUtils.makeMxcPublic(event.content.url)
|
||||||
let filename = event.content.body
|
assert(withoutMxc)
|
||||||
if (event.type === "m.sticker") {
|
const url = `${reg.ooye.bridge_origin}/download/sticker/${withoutMxc}/_.webp`
|
||||||
let mimetype
|
content = `[${event.content.body || "\u2800"}](${url})`
|
||||||
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})
|
|
||||||
|
|
||||||
} else if (event.type === "org.matrix.msc3381.poll.start") {
|
} else if (event.type === "org.matrix.msc3381.poll.start") {
|
||||||
const pollContent = event.content["org.matrix.msc3381.poll.start"] // just for convenience
|
const pollContent = event.content["org.matrix.msc3381.poll.start"] // just for convenience
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ const emojiSheet = sync.require("../../m2d/actions/emoji-sheet")
|
||||||
/** @type {import("../../m2d/converters/emoji-sheet")} */
|
/** @type {import("../../m2d/converters/emoji-sheet")} */
|
||||||
const emojiSheetConverter = sync.require("../../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 = {
|
const schema = {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
server_name: z.string(),
|
server_name: z.string(),
|
||||||
|
|
@ -23,6 +26,10 @@ const schema = {
|
||||||
}),
|
}),
|
||||||
sheet: z.object({
|
sheet: z.object({
|
||||||
e: z.array(z.string()).or(z.string())
|
e: z.array(z.string()).or(z.string())
|
||||||
|
}),
|
||||||
|
sticker: z.object({
|
||||||
|
server_name: z.string().regex(/^[^/]+$/),
|
||||||
|
media_id: z.string().regex(/^[A-Za-z0-9_-]+$/)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,3 +97,14 @@ as.router.get(`/download/sheet`, defineEventHandler(async event => {
|
||||||
setResponseHeader(event, "Content-Type", "image/png")
|
setResponseHeader(event, "Content-Type", "image/png")
|
||||||
return buffer
|
return buffer
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
const stream = await sticker.getAndResizeSticker(`mxc://${mxc}`)
|
||||||
|
setResponseHeader(event, "Content-Type", "image/webp")
|
||||||
|
return stream
|
||||||
|
}))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue