Interpret Matrix media spoilers
This commit is contained in:
@@ -555,25 +555,40 @@ 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
|
// 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")
|
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")) {
|
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")) {
|
||||||
|
// Build message content in addition to the uploaded file
|
||||||
|
const fileIsSpoiler = event.content["page.codeberg.everypizza.msc4193.spoiler"]
|
||||||
|
const fileSpoilerReason = event.content["page.codeberg.everypizza.msc4193.spoiler.reason"]
|
||||||
|
content = ""
|
||||||
|
const captionContent = new mxUtils.MatrixStringBuilder()
|
||||||
|
|
||||||
|
// Caption from Matrix message
|
||||||
|
const fileHasCaption = (event.content.body && event.content.filename && event.content.body !== event.content.filename) || event.content.formatted_body
|
||||||
|
if (fileHasCaption) {
|
||||||
|
captionContent.addLine(event.content.body || "", event.content.formatted_body || tag`${event.content.body || ""}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spoiler message
|
||||||
|
if (fileIsSpoiler && typeof fileSpoilerReason === "string") {
|
||||||
|
captionContent.addLine(`(Spoiler: ${fileSpoilerReason})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// File link as alternative to uploading
|
||||||
if (!("file" in event.content) && event.content.info?.size > getFileSizeForGuild(guild)) {
|
if (!("file" in event.content) && event.content.info?.size > getFileSizeForGuild(guild)) {
|
||||||
// Upload (unencrypted) file as link, because it's too large for Discord
|
// 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.
|
// 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)
|
const url = mxUtils.getPublicUrlForMxc(event.content.url)
|
||||||
assert(url)
|
assert(url)
|
||||||
const filename = event.content.filename || event.content.body
|
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) + " " : ""
|
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>`)
|
if (fileIsSpoiler) {
|
||||||
// Check if the event has a caption that we need to add as well
|
captionContent.addLine(`${emoji}Uploaded SPOILER file: <${url}> (${pb(event.content.info.size)})`, tag`${emoji}<em>Uploaded <strong>SPOILER</strong> file: <span data-mx-spoiler><a href="${url} ">${filename}</a></span> (${pb(event.content.info.size)})</em>`) // the space is necessary to work around a bug in Discord's URL previewer. the preview still gets blurred in the client.
|
||||||
if ((event.content.body && event.content.filename && event.content.body !== event.content.filename) || event.content.formatted_body) {
|
} else {
|
||||||
newText.addLine(event.content.body || "", event.content.formatted_body || tag`${event.content.body || ""}`)
|
captionContent.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>`)
|
||||||
}
|
}
|
||||||
Object.assign(event.content, newText.get())
|
|
||||||
shouldProcessTextEvent = true
|
|
||||||
} else {
|
} else {
|
||||||
// Upload file as file
|
// Upload file as file
|
||||||
content = ""
|
let filename = event.content.filename || event.content.body
|
||||||
const filename = event.content.filename || event.content.body
|
if (fileIsSpoiler) filename = "SPOILER_" + filename
|
||||||
if ("file" in event.content) {
|
if ("file" in event.content) {
|
||||||
// Encrypted
|
// Encrypted
|
||||||
assert.equal(event.content.file.key.alg, "A256CTR")
|
assert.equal(event.content.file.key.alg, "A256CTR")
|
||||||
@@ -584,12 +599,16 @@ async function eventToMessage(event, guild, di) {
|
|||||||
attachments.push({id: "0", filename})
|
attachments.push({id: "0", filename})
|
||||||
pendingFiles.push({name: filename, mxc: event.content.url})
|
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) {
|
|
||||||
|
// Add result to content
|
||||||
|
const result = captionContent.get()
|
||||||
|
if (result.body) {
|
||||||
|
Object.assign(event.content, {body: result.body, format: result.format, formatted_body: result.formatted_body})
|
||||||
shouldProcessTextEvent = true
|
shouldProcessTextEvent = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (event.type === "m.sticker") {
|
if (event.type === "m.sticker") {
|
||||||
content = ""
|
content = ""
|
||||||
let filename = event.content.body
|
let filename = event.content.body
|
||||||
|
|||||||
@@ -404,6 +404,135 @@ test("event2message: spoiler reasons work", async t => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("event2message: media spoilers work", async t => {
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
content: {
|
||||||
|
body: "pitstop.png",
|
||||||
|
filename: "pitstop.png",
|
||||||
|
info: {
|
||||||
|
h: 870,
|
||||||
|
mimetype: "image/png",
|
||||||
|
size: 729990,
|
||||||
|
w: 674,
|
||||||
|
"xyz.amorgan.blurhash": "UqOMmRM{_Mx[xZaxR*tQ.8ayxtWBRkRkWUWB"
|
||||||
|
},
|
||||||
|
msgtype: "m.image",
|
||||||
|
"page.codeberg.everypizza.msc4193.spoiler": true,
|
||||||
|
url: "mxc://agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT"
|
||||||
|
},
|
||||||
|
origin_server_ts: 1764885561299,
|
||||||
|
room_id: "!zq94fae5bVKUubZLp7:agiadn.org",
|
||||||
|
sender: "@underscore_x:agiadn.org",
|
||||||
|
type: "m.room.message",
|
||||||
|
event_id: "$6P7u-lpu2u73ZrHUru2UG1rPfsh8PfYLPK21o3SNIN4",
|
||||||
|
user_id: "@underscore_x:agiadn.org"
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
ensureJoined: [],
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "underscore_x",
|
||||||
|
content: "",
|
||||||
|
avatar_url: undefined,
|
||||||
|
attachments: [{id: "0", filename: "SPOILER_pitstop.png"}],
|
||||||
|
pendingFiles: [{
|
||||||
|
mxc: "mxc://agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT",
|
||||||
|
name: "SPOILER_pitstop.png",
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: media spoilers with reason work", async t => {
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
content: {
|
||||||
|
body: "pitstop.png",
|
||||||
|
filename: "pitstop.png",
|
||||||
|
info: {
|
||||||
|
h: 870,
|
||||||
|
mimetype: "image/png",
|
||||||
|
size: 729990,
|
||||||
|
w: 674,
|
||||||
|
"xyz.amorgan.blurhash": "UqOMmRM{_Mx[xZaxR*tQ.8ayxtWBRkRkWUWB"
|
||||||
|
},
|
||||||
|
msgtype: "m.image",
|
||||||
|
"page.codeberg.everypizza.msc4193.spoiler": true,
|
||||||
|
"page.codeberg.everypizza.msc4193.spoiler.reason": "golden witch solutions",
|
||||||
|
url: "mxc://agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT"
|
||||||
|
},
|
||||||
|
origin_server_ts: 1764885561299,
|
||||||
|
room_id: "!zq94fae5bVKUubZLp7:agiadn.org",
|
||||||
|
sender: "@underscore_x:agiadn.org",
|
||||||
|
type: "m.room.message",
|
||||||
|
event_id: "$6P7u-lpu2u73ZrHUru2UG1rPfsh8PfYLPK21o3SNIN4",
|
||||||
|
user_id: "@underscore_x:agiadn.org"
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
ensureJoined: [],
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "underscore_x",
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles"]
|
||||||
|
},
|
||||||
|
content: "(Spoiler: golden witch solutions)",
|
||||||
|
avatar_url: undefined,
|
||||||
|
attachments: [{id: "0", filename: "SPOILER_pitstop.png"}],
|
||||||
|
pendingFiles: [{
|
||||||
|
mxc: "mxc://agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT",
|
||||||
|
name: "SPOILER_pitstop.png",
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: spoiler files too large for Discord are linked and retain reason", async t => {
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
content: {
|
||||||
|
body: "pitstop.png",
|
||||||
|
filename: "pitstop.png",
|
||||||
|
info: {
|
||||||
|
h: 870,
|
||||||
|
mimetype: "image/png",
|
||||||
|
size: 40000000,
|
||||||
|
w: 674,
|
||||||
|
"xyz.amorgan.blurhash": "UqOMmRM{_Mx[xZaxR*tQ.8ayxtWBRkRkWUWB"
|
||||||
|
},
|
||||||
|
msgtype: "m.image",
|
||||||
|
"page.codeberg.everypizza.msc4193.spoiler": true,
|
||||||
|
"page.codeberg.everypizza.msc4193.spoiler.reason": "golden witch secrets",
|
||||||
|
url: "mxc://agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT"
|
||||||
|
},
|
||||||
|
origin_server_ts: 1764885561299,
|
||||||
|
room_id: "!zq94fae5bVKUubZLp7:agiadn.org",
|
||||||
|
sender: "@underscore_x:agiadn.org",
|
||||||
|
type: "m.room.message",
|
||||||
|
event_id: "$6P7u-lpu2u73ZrHUru2UG1rPfsh8PfYLPK21o3SNIN4",
|
||||||
|
user_id: "@underscore_x:agiadn.org"
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
ensureJoined: [],
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "underscore_x",
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles"]
|
||||||
|
},
|
||||||
|
content: "(Spoiler: golden witch secrets)\n🖼️ _Uploaded **SPOILER** file: ||[pitstop.png](https://bridge.example.org/download/matrix/agiadn.org/JY5NvEFojTvYDp5znjGIkkQ7Ez7GwsdT )|| (40 MB)_",
|
||||||
|
avatar_url: undefined
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test("event2message: markdown syntax is escaped", async t => {
|
test("event2message: markdown syntax is escaped", async t => {
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
await eventToMessage({
|
await eventToMessage({
|
||||||
@@ -4236,7 +4365,7 @@ test("event2message: files too large for Discord can have a plaintext caption",
|
|||||||
messagesToEdit: [],
|
messagesToEdit: [],
|
||||||
messagesToSend: [{
|
messagesToSend: [{
|
||||||
username: "cadence [they]",
|
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",
|
content: "Cat emoji surrounded by pink hearts\n🖼️ _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",
|
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: ["users", "roles"]
|
parse: ["users", "roles"]
|
||||||
@@ -4283,7 +4412,7 @@ test("event2message: files too large for Discord can have a formatted caption",
|
|||||||
messagesToEdit: [],
|
messagesToEdit: [],
|
||||||
messagesToSend: [{
|
messagesToSend: [{
|
||||||
username: "cadence [they]",
|
username: "cadence [they]",
|
||||||
content: "🖼️ _Uploaded file: [5740.jpg](https://bridge.example.org/download/matrix/thomcat.rocks/RTHsXmcMPXmuHqVNsnbKtRbh) (40 MB)_\nthis event has `formatting`",
|
content: "this event has `formatting`\n🖼️ _Uploaded file: [5740.jpg](https://bridge.example.org/download/matrix/thomcat.rocks/RTHsXmcMPXmuHqVNsnbKtRbh) (40 MB)_",
|
||||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: ["users", "roles"]
|
parse: ["users", "roles"]
|
||||||
|
|||||||
@@ -170,7 +170,8 @@ INSERT INTO member_cache (room_id, mxid, displayname, avatar_url, power_level) V
|
|||||||
('!TqlyQmifxGUggEmdBN:cadence.moe', '@ampflower:matrix.org', 'Ampflower 🌺', 'mxc://cadence.moe/PRfhXYBTOalvgQYtmCLeUXko', 0),
|
('!TqlyQmifxGUggEmdBN:cadence.moe', '@ampflower:matrix.org', 'Ampflower 🌺', 'mxc://cadence.moe/PRfhXYBTOalvgQYtmCLeUXko', 0),
|
||||||
('!TqlyQmifxGUggEmdBN:cadence.moe', '@aflower:syndicated.gay', 'Rose', 'mxc://syndicated.gay/ZkBUPXCiXTjdJvONpLJmcbKP', 0),
|
('!TqlyQmifxGUggEmdBN:cadence.moe', '@aflower:syndicated.gay', 'Rose', 'mxc://syndicated.gay/ZkBUPXCiXTjdJvONpLJmcbKP', 0),
|
||||||
('!TqlyQmifxGUggEmdBN:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL, 0),
|
('!TqlyQmifxGUggEmdBN:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL, 0),
|
||||||
('!iSyXgNxQcEuXoXpsSn:pussthecat.org', '@austin:tchncs.de', 'Austin Huang', 'mxc://tchncs.de/090a2b5e07eed2f71e84edad5207221e6c8f8b8e', 0);
|
('!iSyXgNxQcEuXoXpsSn:pussthecat.org', '@austin:tchncs.de', 'Austin Huang', 'mxc://tchncs.de/090a2b5e07eed2f71e84edad5207221e6c8f8b8e', 0),
|
||||||
|
('!zq94fae5bVKUubZLp7:agiadn.org', '@underscore_x:agiadn.org', 'underscore_x', NULL, 100);
|
||||||
|
|
||||||
INSERT INTO reaction (hashed_event_id, message_id, encoded_emoji) VALUES
|
INSERT INTO reaction (hashed_event_id, message_id, encoded_emoji) VALUES
|
||||||
(5162930312280790092, '1141501302736695317', '%F0%9F%90%88');
|
(5162930312280790092, '1141501302736695317', '%F0%9F%90%88');
|
||||||
|
|||||||
Reference in New Issue
Block a user