Spoiler Fix #94
4 changed files with 31 additions and 16 deletions
|
|
@ -135,7 +135,8 @@ const embedTitleParser = markdown.markdownEngine.parserFor({
|
||||||
* @param {{fetch?: typeof fetch}} [di]
|
* @param {{fetch?: typeof fetch}} [di]
|
||||||
*/
|
*/
|
||||||
async function attachmentToEvent(mentions, attachment, alwaysLink, di) {
|
async function attachmentToEvent(mentions, attachment, alwaysLink, di) {
|
||||||
const external_url = dUtils.getPublicUrlForCdn(attachment.url)
|
const spoiler = attachment.filename.startsWith("SPOILER_") || !!((attachment.flags || 0) & DiscordTypes.AttachmentFlags.IsSpoiler);
|
||||||
|
const external_url = dUtils.getPublicUrlForCdn(attachment.url, spoiler ? "spoiler/" : "")
|
||||||
const emoji =
|
const emoji =
|
||||||
attachment.content_type?.startsWith("image/jp") ? "📸"
|
attachment.content_type?.startsWith("image/jp") ? "📸"
|
||||||
: attachment.content_type?.startsWith("image/") ? "🖼️"
|
: attachment.content_type?.startsWith("image/") ? "🖼️"
|
||||||
|
|
@ -143,15 +144,15 @@ async function attachmentToEvent(mentions, attachment, alwaysLink, di) {
|
||||||
: attachment.content_type?.startsWith("text/") ? "📝"
|
: attachment.content_type?.startsWith("text/") ? "📝"
|
||||||
: attachment.content_type?.startsWith("audio/") ? "🎶"
|
: attachment.content_type?.startsWith("audio/") ? "🎶"
|
||||||
: "📄"
|
: "📄"
|
||||||
// no native media spoilers in Element, so we'll post a link instead, forcing it to not preview using a blockquote
|
// no native media spoilers in Element, so we'll post a spoiler (unpreviewable) link instead
|
||||||
if (attachment.filename.startsWith("SPOILER_") || !!((attachment.flags || 0) & DiscordTypes.AttachmentFlags.IsSpoiler)) {
|
if (spoiler) {
|
||||||
return {
|
return {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": mentions,
|
"m.mentions": mentions,
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: `${emoji} Uploaded SPOILER file: ${external_url} (${pb(attachment.size)})`,
|
body: `${emoji} Uploaded SPOILER file: ${external_url} (${pb(attachment.size)})`,
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `<blockquote>${emoji} Uploaded SPOILER file: <a href="${external_url}">${external_url}</a> (${pb(attachment.size)})</blockquote>`
|
formatted_body: `${emoji} Uploaded <strong>SPOILER</strong> file: <a href="${external_url}">${external_url}</a> (${pb(attachment.size)})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// attempt to convert large PNG image files to JPEG, since it's almost certainly a photo that was forced into PNG by the user copy-pasting.
|
// attempt to convert large PNG image files to JPEG, since it's almost certainly a photo that was forced into PNG by the user copy-pasting.
|
||||||
|
|
|
||||||
|
|
@ -366,9 +366,9 @@ test("message2event: spoiler attachment", async t => {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "📄 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci (74 KB)",
|
body: "📄 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/1100319550446252084/spoiler/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci (74 KB)",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: "<blockquote>📄 Uploaded SPOILER file: <a href=\"https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci\">https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci</a> (74 KB)</blockquote>"
|
formatted_body: "📄 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/1100319550446252084/spoiler/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci\">https://bridge.example.org/download/discordcdn/1100319550446252084/spoiler/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci</a> (74 KB)"
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -1205,13 +1205,13 @@ test("message2event: multiple attachments are combined into the same event where
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "hey"
|
body: "hey"
|
||||||
+ "\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)"
|
+ "\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)"
|
||||||
+ "\n📸 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg (38 KB)"
|
+ "\n📸 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/123/spoiler/456/SPOILER_secret.jpg (38 KB)"
|
||||||
+ "\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)",
|
+ "\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: "hey"
|
formatted_body: "hey"
|
||||||
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
||||||
+ `<br><blockquote>📸 Uploaded SPOILER file: <a href="https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg">https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg</a> (38 KB)</blockquote>`
|
+ `<br>📸 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/123/spoiler/456/SPOILER_secret.jpg\">https://bridge.example.org/download/discordcdn/123/spoiler/456/SPOILER_secret.jpg</a> (38 KB)`
|
||||||
+ `📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
||||||
}, {
|
}, {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
|
|
|
||||||
|
|
@ -134,14 +134,17 @@ function timestampToSnowflakeInexact(timestamp) {
|
||||||
return String((timestamp - EPOCH) * 2**22)
|
return String((timestamp - EPOCH) * 2**22)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {string} url */
|
/**
|
||||||
function getPublicUrlForCdn(url) {
|
* @param {string} url
|
||||||
|
* @param {""|"spoiler/"} spoiler
|
||||||
|
*/
|
||||||
|
function getPublicUrlForCdn(url, spoiler="") {
|
||||||
const match = url.match(/https:\/\/(cdn|media)\.discordapp\.(?:com|net)\/attachments\/([0-9]+)\/([0-9]+)\/([-A-Za-z0-9_.,]+)/)
|
const match = url.match(/https:\/\/(cdn|media)\.discordapp\.(?:com|net)\/attachments\/([0-9]+)\/([0-9]+)\/([-A-Za-z0-9_.,]+)/)
|
||||||
if (!match) return url
|
if (!match) return url
|
||||||
const unsignedHash = hasher.h64(match[3]) // attachment ID
|
const unsignedHash = hasher.h64(match[3]) // attachment ID
|
||||||
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
||||||
db.prepare("INSERT OR IGNORE INTO media_proxy (permitted_hash) VALUES (?)").run(signedHash)
|
db.prepare("INSERT OR IGNORE INTO media_proxy (permitted_hash) VALUES (?)").run(signedHash)
|
||||||
return `${reg.ooye.bridge_origin}/download/discord${match[1]}/${match[2]}/${match[3]}/${match[4]}`
|
return `${reg.ooye.bridge_origin}/download/discord${match[1]}/${match[2]}/${spoiler}${match[3]}/${match[4]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ let hasher = null
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
require("xxhash-wasm")().then(h => hasher = h)
|
require("xxhash-wasm")().then(h => hasher = h)
|
||||||
|
|
||||||
const {discord, as, select} = require("../../passthrough")
|
const {discord, as, select, sync} = require("../../passthrough")
|
||||||
|
|
||||||
|
/** @type {import("../web-utils")} */
|
||||||
|
const webUtils = sync.require("../web-utils")
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
|
|
@ -40,10 +43,16 @@ function timeUntilExpiry(url) {
|
||||||
if (time > 0) return time
|
if (time > 0) return time
|
||||||
}
|
}
|
||||||
|
|
||||||
function defineMediaProxyHandler(domain) {
|
/**
|
||||||
|
* @param {string} domain
|
||||||
|
* @param {boolean} spoiler
|
||||||
|
*/
|
||||||
|
function defineMediaProxyHandler(domain, spoiler) {
|
||||||
return defineEventHandler(async event => {
|
return defineEventHandler(async event => {
|
||||||
const params = await getValidatedRouterParams(event, schema.params.parse)
|
const params = await getValidatedRouterParams(event, schema.params.parse)
|
||||||
|
|
||||||
|
if (spoiler) webUtils.rejectUrlPreviewer(event)
|
||||||
|
|
||||||
const unsignedHash = hasher.h64(params.attachment_id)
|
const unsignedHash = hasher.h64(params.attachment_id)
|
||||||
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
||||||
|
|
||||||
|
|
@ -81,7 +90,9 @@ function defineMediaProxyHandler(domain) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
as.router.get(`/download/discordcdn/:channel_id/:attachment_id/:file_name`, defineMediaProxyHandler("cdn.discordapp.com"))
|
as.router.get(`/download/discordcdn/:channel_id/:attachment_id/:file_name`, defineMediaProxyHandler("cdn.discordapp.com", false))
|
||||||
as.router.get(`/download/discordmedia/:channel_id/:attachment_id/:file_name`, defineMediaProxyHandler("media.discordapp.net"))
|
as.router.get(`/download/discordmedia/:channel_id/:attachment_id/:file_name`, defineMediaProxyHandler("media.discordapp.net", false))
|
||||||
|
as.router.get(`/download/discordcdn/:channel_id/spoiler/:attachment_id/:file_name`, defineMediaProxyHandler("cdn.discordapp.com", true))
|
||||||
|
as.router.get(`/download/discordmedia/:channel_id/spoiler/:attachment_id/:file_name`, defineMediaProxyHandler("media.discordapp.net", true))
|
||||||
|
|
||||||
module.exports._cache = cache
|
module.exports._cache = cache
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue