m->d: link too-large files instead of uploading
This commit is contained in:
@@ -8,6 +8,8 @@ const TurndownService = require("@cloudrac3r/turndown")
|
||||
const domino = require("domino")
|
||||
const assert = require("assert").strict
|
||||
const entities = require("entities")
|
||||
const pb = require("prettier-bytes")
|
||||
const {tag} = require("@cloudrac3r/html-template-tag")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {sync, db, discord, select, from} = passthrough
|
||||
@@ -483,6 +485,17 @@ const attachmentEmojis = new Map([
|
||||
["m.file", "📄"]
|
||||
])
|
||||
|
||||
/** @param {DiscordTypes.APIGuild} guild */
|
||||
function getFileSizeForGuild(guild) {
|
||||
// guild.features may include strings such as MAX_FILE_SIZE_50_MB and MAX_FILE_SIZE_100_MB, which are the current server boost amounts
|
||||
const fileSizeFeature = guild?.features.map(f => Number(f.match(/^MAX_FILE_SIZE_([0-9]+)_MB$/)?.[1])).filter(f => f).sort()[0]
|
||||
if (fileSizeFeature) {
|
||||
return fileSizeFeature * 1024 * 1024 // discord uses big megabytes
|
||||
} else {
|
||||
return 10 * 1024 * 1024 // default file size is 10 MB
|
||||
}
|
||||
}
|
||||
|
||||
async function getL1L2ReplyLine(called = false) {
|
||||
// @ts-ignore
|
||||
const autoEmoji = new Map(select("auto_emoji", ["name", "emoji_id"], {}, "WHERE name = 'L1' OR name = 'L2'").raw().all())
|
||||
@@ -539,21 +552,39 @@ async function eventToMessage(event, guild, di) {
|
||||
// Handle images first - might need to handle their `body`/`formatted_body` as well, which will fall through to the text processor
|
||||
let shouldProcessTextEvent = event.type === "m.room.message" && (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote")
|
||||
if (event.type === "m.room.message" && (event.content.msgtype === "m.file" || event.content.msgtype === "m.video" || event.content.msgtype === "m.audio" || event.content.msgtype === "m.image")) {
|
||||
content = ""
|
||||
const filename = event.content.filename || event.content.body
|
||||
if ("file" in event.content) {
|
||||
// Encrypted
|
||||
assert.equal(event.content.file.key.alg, "A256CTR")
|
||||
attachments.push({id: "0", filename})
|
||||
pendingFiles.push({name: filename, mxc: event.content.file.url, key: event.content.file.key.k, iv: event.content.file.iv})
|
||||
} else {
|
||||
// Unencrypted
|
||||
attachments.push({id: "0", filename})
|
||||
pendingFiles.push({name: filename, mxc: event.content.url})
|
||||
}
|
||||
// Check if we also need to process a text event for this image - if it has a caption that's different from its filename
|
||||
if ((event.content.body && event.content.filename && event.content.body !== event.content.filename) || event.content.formatted_body) {
|
||||
if (!("file" in event.content) && event.content.info?.size > getFileSizeForGuild(guild)) {
|
||||
// Upload (unencrypted) file as link, because it's too large for Discord
|
||||
// Do this by constructing a sample Matrix message with the link and then use the text processor to convert that + the original caption.
|
||||
const url = mxUtils.getPublicUrlForMxc(event.content.url)
|
||||
assert(url)
|
||||
const filename = event.content.filename || event.content.body
|
||||
const newText = new mxUtils.MatrixStringBuilder()
|
||||
const emoji = attachmentEmojis.has(event.content.msgtype) ? attachmentEmojis.get(event.content.msgtype) + " " : ""
|
||||
newText.addLine(`${emoji}Uploaded file: ${url} (${pb(event.content.info.size)})`, tag`${emoji}<em>Uploaded file: <a href="${url}">${filename}</a> (${pb(event.content.info.size)})</em>`)
|
||||
// Check if the event has a caption that we need to add as well
|
||||
if ((event.content.body && event.content.filename && event.content.body !== event.content.filename) || event.content.formatted_body) {
|
||||
newText.addLine(event.content.body || "", event.content.formatted_body || tag`${event.content.body || ""}`)
|
||||
}
|
||||
Object.assign(event.content, newText.get())
|
||||
shouldProcessTextEvent = true
|
||||
} else {
|
||||
// Upload file as file
|
||||
content = ""
|
||||
const filename = event.content.filename || event.content.body
|
||||
if ("file" in event.content) {
|
||||
// Encrypted
|
||||
assert.equal(event.content.file.key.alg, "A256CTR")
|
||||
attachments.push({id: "0", filename})
|
||||
pendingFiles.push({name: filename, mxc: event.content.file.url, key: event.content.file.key.k, iv: event.content.file.iv})
|
||||
} else {
|
||||
// Unencrypted
|
||||
attachments.push({id: "0", filename})
|
||||
pendingFiles.push({name: filename, mxc: event.content.url})
|
||||
}
|
||||
// Check if we also need to process a text event for this image - if it has a caption that's different from its filename
|
||||
if ((event.content.body && event.content.filename && event.content.body !== event.content.filename) || event.content.formatted_body) {
|
||||
shouldProcessTextEvent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.type === "m.sticker") {
|
||||
|
||||
@@ -4041,6 +4041,166 @@ test("event2message: evil encrypted image attachment works", async t => {
|
||||
)
|
||||
})
|
||||
|
||||
test("event2message: large attachments are uploaded if the server boost level is sufficient", async t => {
|
||||
t.deepEqual(
|
||||
await eventToMessage({
|
||||
type: "m.room.message",
|
||||
sender: "@cadence:cadence.moe",
|
||||
content: {
|
||||
body: "cool cat.png",
|
||||
filename: "cool cat.png",
|
||||
info: {
|
||||
size: 90_000_000,
|
||||
mimetype: "image/png",
|
||||
w: 480,
|
||||
h: 480,
|
||||
"xyz.amorgan.blurhash": "URTHsVaTpdj2eKZgkkkXp{pHl7feo@lSl9Z$"
|
||||
},
|
||||
msgtype: "m.image",
|
||||
url: "mxc://cadence.moe/IvxVJFLEuksCNnbojdSIeEvn"
|
||||
},
|
||||
event_id: "$CXQy3Wmg1A-gL_xAesC1HQcQTEXwICLdSwwUx55FBTI",
|
||||
room_id: "!BnKuBPCvyfOkhcUjEu:cadence.moe"
|
||||
}, {
|
||||
features: ["MAX_FILE_SIZE_100_MB"]
|
||||
}),
|
||||
{
|
||||
ensureJoined: [],
|
||||
messagesToDelete: [],
|
||||
messagesToEdit: [],
|
||||
messagesToSend: [{
|
||||
username: "cadence [they]",
|
||||
content: "",
|
||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||
attachments: [{id: "0", filename: "cool cat.png"}],
|
||||
pendingFiles: [{name: "cool cat.png", mxc: "mxc://cadence.moe/IvxVJFLEuksCNnbojdSIeEvn"}]
|
||||
}]
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test("event2message: files too large for Discord are linked as as URL", async t => {
|
||||
t.deepEqual(
|
||||
await eventToMessage({
|
||||
type: "m.room.message",
|
||||
sender: "@cadence:cadence.moe",
|
||||
content: {
|
||||
body: "cool cat.png",
|
||||
filename: "cool cat.png",
|
||||
info: {
|
||||
size: 40_000_000,
|
||||
mimetype: "image/png",
|
||||
w: 480,
|
||||
h: 480,
|
||||
"xyz.amorgan.blurhash": "URTHsVaTpdj2eKZgkkkXp{pHl7feo@lSl9Z$"
|
||||
},
|
||||
msgtype: "m.image",
|
||||
url: "mxc://cadence.moe/IvxVJFLEuksCNnbojdSIeEvn"
|
||||
},
|
||||
event_id: "$CXQy3Wmg1A-gL_xAesC1HQcQTEXwICLdSwwUx55FBTI",
|
||||
room_id: "!BnKuBPCvyfOkhcUjEu:cadence.moe"
|
||||
}),
|
||||
{
|
||||
ensureJoined: [],
|
||||
messagesToDelete: [],
|
||||
messagesToEdit: [],
|
||||
messagesToSend: [{
|
||||
username: "cadence [they]",
|
||||
content: "🖼️ _Uploaded file: [cool cat.png](https://bridge.example.org/download/matrix/cadence.moe/IvxVJFLEuksCNnbojdSIeEvn) (40 MB)_",
|
||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||
allowed_mentions: {
|
||||
parse: ["users", "roles"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test("event2message: files too large for Discord can have a plaintext caption", async t => {
|
||||
t.deepEqual(
|
||||
await eventToMessage({
|
||||
type: "m.room.message",
|
||||
sender: "@cadence:cadence.moe",
|
||||
content: {
|
||||
body: "Cat emoji surrounded by pink hearts",
|
||||
filename: "cool cat.png",
|
||||
info: {
|
||||
size: 40_000_000,
|
||||
mimetype: "image/png",
|
||||
w: 480,
|
||||
h: 480,
|
||||
"xyz.amorgan.blurhash": "URTHsVaTpdj2eKZgkkkXp{pHl7feo@lSl9Z$"
|
||||
},
|
||||
msgtype: "m.image",
|
||||
url: "mxc://cadence.moe/IvxVJFLEuksCNnbojdSIeEvn"
|
||||
},
|
||||
event_id: "$CXQy3Wmg1A-gL_xAesC1HQcQTEXwICLdSwwUx55FBTI",
|
||||
room_id: "!BnKuBPCvyfOkhcUjEu:cadence.moe"
|
||||
}),
|
||||
{
|
||||
ensureJoined: [],
|
||||
messagesToDelete: [],
|
||||
messagesToEdit: [],
|
||||
messagesToSend: [{
|
||||
username: "cadence [they]",
|
||||
content: "🖼️ _Uploaded file: [cool cat.png](https://bridge.example.org/download/matrix/cadence.moe/IvxVJFLEuksCNnbojdSIeEvn) (40 MB)_\nCat emoji surrounded by pink hearts",
|
||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||
allowed_mentions: {
|
||||
parse: ["users", "roles"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test("event2message: files too large for Discord can have a formatted caption", async t => {
|
||||
t.deepEqual(
|
||||
await eventToMessage({
|
||||
content: {
|
||||
body: "this event has `formatting`",
|
||||
filename: "5740.jpg",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "this event has <code>formatting</code>",
|
||||
info: {
|
||||
h: 1340,
|
||||
mimetype: "image/jpeg",
|
||||
size: 40_000_000,
|
||||
thumbnail_info: {
|
||||
h: 670,
|
||||
mimetype: "image/jpeg",
|
||||
size: 80157,
|
||||
w: 540
|
||||
},
|
||||
thumbnail_url: "mxc://thomcat.rocks/XhLsOCDBYyearsLQgUUrbAvw",
|
||||
w: 1080,
|
||||
"xyz.amorgan.blurhash": "KHJQG*55ic-.}?0M58J.9v"
|
||||
},
|
||||
msgtype: "m.image",
|
||||
url: "mxc://thomcat.rocks/RTHsXmcMPXmuHqVNsnbKtRbh"
|
||||
},
|
||||
origin_server_ts: 1740607766895,
|
||||
sender: "@cadence:cadence.moe",
|
||||
type: "m.room.message",
|
||||
event_id: "$NqNqVgukiQm1nynm9vIr9FIq31hZpQ3udOd7cBIW46U",
|
||||
room_id: "!BnKuBPCvyfOkhcUjEu:cadence.moe"
|
||||
}),
|
||||
{
|
||||
ensureJoined: [],
|
||||
messagesToDelete: [],
|
||||
messagesToEdit: [],
|
||||
messagesToSend: [{
|
||||
username: "cadence [they]",
|
||||
content: "🖼️ _Uploaded file: [5740.jpg](https://bridge.example.org/download/matrix/thomcat.rocks/RTHsXmcMPXmuHqVNsnbKtRbh) (40 MB)_\nthis event has `formatting`",
|
||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||
allowed_mentions: {
|
||||
parse: ["users", "roles"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
test("event2message: stickers work", async t => {
|
||||
t.deepEqual(
|
||||
await eventToMessage({
|
||||
|
||||
Reference in New Issue
Block a user