Spoiler Fix #90

Manually merged
cadence merged 11 commits from Guzio/out-of-your-element:spoilerfix into main 2026-10-20 12:45:17 +00:00
4 changed files with 122 additions and 116 deletions
Showing only changes of commit 15055b8ee0 - Show all commits

Use less hacky way of setting the params

Guzio 2026-10-20 13:22:18 +02:00

View file

@ -136,7 +136,7 @@ const embedTitleParser = markdown.markdownEngine.parserFor({
*/ */
async function attachmentToEvent(mentions, attachment, alwaysLink, di) { async function attachmentToEvent(mentions, attachment, alwaysLink, di) {
const spoiler = attachment.filename.startsWith("SPOILER_") || !!((attachment.flags || 0) & DiscordTypes.AttachmentFlags.IsSpoiler); const spoiler = attachment.filename.startsWith("SPOILER_") || !!((attachment.flags || 0) & DiscordTypes.AttachmentFlags.IsSpoiler);
const external_url = dUtils.getPublicUrlForCdn(attachment.url, spoiler ? "?spoiler" : "") const external_url = dUtils.getPublicUrlForCdn(attachment.url, 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/") ? "🖼️"

View file

@ -1,10 +1,10 @@
const {test} = require("supertape") const { test } = require("supertape")
const {messageToEvent} = require("./message-to-event") const { messageToEvent } = require("./message-to-event")
const {MatrixServerError} = require("../../matrix/mreq") const { MatrixServerError } = require("../../matrix/mreq")
const data = require("../../../test/data") const data = require("../../../test/data")
const {mockGetEffectivePower} = require("../../matrix/utils.test") const { mockGetEffectivePower } = require("../../matrix/utils.test")
const Ty = require("../../types") const Ty = require("../../types")
const {db} = require("../../passthrough") const { db } = require("../../passthrough")
/** /**
* @param {string} roomID * @param {string} roomID
@ -12,7 +12,7 @@ const {db} = require("../../passthrough")
* @returns {(roomID: string, eventID: string) => Promise<Ty.Event.Outer<Ty.Event.M_Room_Message>>} * @returns {(roomID: string, eventID: string) => Promise<Ty.Event.Outer<Ty.Event.M_Room_Message>>}
*/ */
function mockGetEvent(t, roomID_in, eventID_in, outer) { function mockGetEvent(t, roomID_in, eventID_in, outer) {
return async function(roomID, eventID) { return async function (roomID, eventID) {
t.equal(roomID, roomID_in) t.equal(roomID, roomID_in)
t.equal(eventID, eventID_in) t.equal(eventID, eventID_in)
return new Promise(resolve => { return new Promise(resolve => {
@ -74,8 +74,8 @@ test("message2event: simple room mention", async t => {
t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe") t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -102,8 +102,8 @@ test("message2event: simple room link", async t => {
t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe") t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -130,8 +130,8 @@ test("message2event: nicked room mention", async t => {
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -234,8 +234,8 @@ test("message2event: simple message link", async t => {
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:super.invalid": {display_name: null, avatar_url: null} "@user:super.invalid": { display_name: null, avatar_url: null }
} }
} }
} }
@ -270,8 +270,8 @@ test("message2event: message link that OOYE doesn't know about", async t => {
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -308,8 +308,8 @@ test("message2event: message timestamp failed to fetch", async t => {
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -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?spoiler (74 KB)", body: "📄 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci?spoiler= (74 KB)",
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: "📄 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci?spoiler\">https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci?spoiler</a> (74 KB)" formatted_body: "📄 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci?spoiler=\">https://bridge.example.org/download/discordcdn/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci?spoiler=</a> (74 KB)"
}]) }])
}) })
@ -505,7 +505,7 @@ test("message2event: simple reply to matrix user", async t => {
}) })
test("message2event: simple reply to matrix user, reply fallbacks disabled", async t => { test("message2event: simple reply to matrix user, reply fallbacks disabled", async t => {
const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {includeReplyFallback: false}, { const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, { includeReplyFallback: false }, {
api: { api: {
getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", { getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", {
type: "m.room.message", type: "m.room.message",
@ -605,17 +605,17 @@ test("message2event: voice message", async t => {
const events = await messageToEvent(data.message.voice_message) const events = await messageToEvent(data.message.voice_message)
t.deepEqual(events, [{ t.deepEqual(events, [{
$type: "m.room.message", $type: "m.room.message",
body: "voice-message.ogg", body: "voice-message.ogg",
external_url: "https://bridge.example.org/download/discordcdn/1099031887500034088/1112476845502365786/voice-message.ogg", external_url: "https://bridge.example.org/download/discordcdn/1099031887500034088/1112476845502365786/voice-message.ogg",
filename: "voice-message.ogg", filename: "voice-message.ogg",
info: { info: {
duration: 3960, duration: 3960,
mimetype: "audio/ogg", mimetype: "audio/ogg",
size: 10584, size: 10584,
}, },
"m.mentions": {}, "m.mentions": {},
msgtype: "m.audio", msgtype: "m.audio",
url: "mxc://cadence.moe/MRRPDggXQMYkrUjTpxQbmcxB" url: "mxc://cadence.moe/MRRPDggXQMYkrUjTpxQbmcxB"
}]) }])
}) })
@ -628,16 +628,16 @@ test("message2event: misc file", async t => {
"m.mentions": {} "m.mentions": {}
}, { }, {
$type: "m.room.message", $type: "m.room.message",
body: "the.yml", body: "the.yml",
external_url: "https://bridge.example.org/download/discordcdn/122155380120748034/1174514575220158545/the.yml", external_url: "https://bridge.example.org/download/discordcdn/122155380120748034/1174514575220158545/the.yml",
filename: "the.yml", filename: "the.yml",
info: { info: {
mimetype: "text/plain; charset=utf-8", mimetype: "text/plain; charset=utf-8",
size: 2274 size: 2274
}, },
"m.mentions": {}, "m.mentions": {},
msgtype: "m.file", msgtype: "m.file",
url: "mxc://cadence.moe/HnQIYQmmlIKwOQsbFsIGpzPP" url: "mxc://cadence.moe/HnQIYQmmlIKwOQsbFsIGpzPP"
}]) }])
}) })
@ -674,7 +674,7 @@ test("message2event: simple reply in thread to a matrix user's reply", async t =
user_ids: ["@cadence:cadence.moe"] user_ids: ["@cadence:cadence.moe"]
}, },
msgtype: "m.text", msgtype: "m.text",
body: "Well, they don't seem to..." body: "Well, they don't seem to..."
}]) }])
}) })
@ -711,7 +711,7 @@ test("message2event: infinidoge's reply to ami's matrix smalltext reply to infin
user_ids: ["@ami:the-apothecary.club"] user_ids: ["@ami:the-apothecary.club"]
}, },
msgtype: "m.text", msgtype: "m.text",
body: `Most likely` body: `Most likely`
}]) }])
}) })
@ -748,7 +748,7 @@ test("message2event: infinidoge's reply to ami's matrix smalltext singleline rep
user_ids: ["@ami:the-apothecary.club"] user_ids: ["@ami:the-apothecary.club"]
}, },
msgtype: "m.text", msgtype: "m.text",
body: `Most likely` body: `Most likely`
}]) }])
}) })
@ -757,11 +757,11 @@ test("message2event: reply to a Discord message that wasn't bridged", async t =>
t.deepEqual(events, [{ t.deepEqual(events, [{
$type: "m.room.message", $type: "m.room.message",
msgtype: "m.text", msgtype: "m.text",
body: `> In reply to a 1-day-old unbridged message:` body: `> In reply to a 1-day-old unbridged message:`
+ `\n> Occimyy: BILLY BOB THE GREAT` + `\n> Occimyy: BILLY BOB THE GREAT`
+ `\n\nenigmatic`, + `\n\nenigmatic`,
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: `<blockquote>In reply to a 1-day-old unbridged message from Occimyy:<br>BILLY BOB THE GREAT</blockquote>enigmatic`, formatted_body: `<blockquote>In reply to a 1-day-old unbridged message from Occimyy:<br>BILLY BOB THE GREAT</blockquote>enigmatic`,
"m.mentions": {} "m.mentions": {}
}]) }])
}) })
@ -1151,10 +1151,12 @@ test("message2event: very large png is converted to jpeg for upload", async t =>
async fetch(url, init) { async fetch(url, init) {
t.equal(url, "https://media.discordapp.net/attachments/1160894080998461480/1548656385292509274/855064e9-bdae-487a-a497-c662bd510487.png?format=jpeg") t.equal(url, "https://media.discordapp.net/attachments/1160894080998461480/1548656385292509274/855064e9-bdae-487a-a497-c662bd510487.png?format=jpeg")
t.equal(init.method, "HEAD") t.equal(init.method, "HEAD")
return new Response("wa", {headers: { return new Response("wa", {
"Content-Type": "image/jpeg", headers: {
"Content-Length": 483085 "Content-Type": "image/jpeg",
}}) "Content-Length": 483085
}
})
} }
}) })
t.deepEqual(events, [{ t.deepEqual(events, [{
@ -1205,12 +1207,12 @@ 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?spoiler (38 KB)" + "\n📸 Uploaded SPOILER file: https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg?spoiler= (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>📸 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg?spoiler\">https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg?spoiler</a> (38 KB)` + `<br>📸 Uploaded <strong>SPOILER</strong> file: <a href=\"https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg?spoiler=\">https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg?spoiler=</a> (38 KB)`
+ `<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)`
}, { }, {
$type: "m.room.message", $type: "m.room.message",
@ -1332,7 +1334,7 @@ test("message2event: emoji triple long name", async t => {
body: ":brillillillilliant_move::brillillillilliant_move::brillillillilliant_move:", body: ":brillillillilliant_move::brillillillilliant_move::brillillillilliant_move:",
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: formatted_body:
'<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">' '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
+ '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">' + '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
+ '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">' + '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
}]) }])
@ -1446,8 +1448,8 @@ test("message2event: constructed forwarded message", async t => {
async getJoinedMembers() { async getJoinedMembers() {
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -1505,8 +1507,8 @@ test("message2event: constructed forwarded text", async t => {
async getJoinedMembers() { async getJoinedMembers() {
return { return {
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
"@user:matrix.org": {display_name: null, avatar_url: null} "@user:matrix.org": { display_name: null, avatar_url: null }
} }
} }
} }
@ -1550,10 +1552,10 @@ test("message2event: don't scan forwarded messages for mentions", async t => {
}) })
test("message2event: invite no details embed if no event", async t => { test("message2event: invite no details embed if no event", async t => {
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381190945646710824"}, {}, {}, { const events = await messageToEvent({ content: "https://discord.gg/placeholder?event=1381190945646710824" }, {}, {}, {
snow: { snow: {
invite: { invite: {
getInvite: async () => ({...data.invite.irl, guild_scheduled_event: null}) getInvite: async () => ({ ...data.invite.irl, guild_scheduled_event: null })
} }
} }
}) })
@ -1570,7 +1572,7 @@ test("message2event: invite no details embed if no event", async t => {
}) })
test("message2event: irl invite event renders embed", async t => { test("message2event: irl invite event renders embed", async t => {
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381190945646710824"}, {}, {}, { const events = await messageToEvent({ content: "https://discord.gg/placeholder?event=1381190945646710824" }, {}, {}, {
snow: { snow: {
invite: { invite: {
getInvite: async () => data.invite.irl getInvite: async () => data.invite.irl
@ -1603,7 +1605,7 @@ test("message2event: irl invite event renders embed", async t => {
}) })
test("message2event: vc invite event renders embed", async t => { test("message2event: vc invite event renders embed", async t => {
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381174024801095751"}, {}, {}, { const events = await messageToEvent({ content: "https://discord.gg/placeholder?event=1381174024801095751" }, {}, {}, {
snow: { snow: {
invite: { invite: {
getInvite: async () => data.invite.vc getInvite: async () => data.invite.vc
@ -1637,12 +1639,12 @@ test("message2event: vc invite event renders embed", async t => {
}) })
test("message2event: vc invite event renders embed with room link", async t => { test("message2event: vc invite event renders embed with room link", async t => {
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381174024801095751"}, {}, {}, { const events = await messageToEvent({ content: "https://discord.gg/placeholder?event=1381174024801095751" }, {}, {}, {
api: { api: {
getEffectivePower: mockGetEffectivePower(), getEffectivePower: mockGetEffectivePower(),
getJoinedMembers: async () => ({ getJoinedMembers: async () => ({
joined: { joined: {
"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, "@_ooye_bot:cadence.moe": { display_name: null, avatar_url: null },
} }
}) })
}, },
@ -1679,7 +1681,7 @@ test("message2event: vc invite event renders embed with room link", async t => {
}) })
test("message2event: expired/invalid invites are sent as-is", async t => { test("message2event: expired/invalid invites are sent as-is", async t => {
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381190945646710824"}, {}, {}, { const events = await messageToEvent({ content: "https://discord.gg/placeholder?event=1381190945646710824" }, {}, {}, {
snow: { snow: {
invite: { invite: {
async getInvite() { async getInvite() {
@ -1704,11 +1706,11 @@ test("message2event: channel links are converted even inside lists (parser post-
let called = 0 let called = 0
const events = await messageToEvent({ const events = await messageToEvent({
content: "1. Don't be a dick" content: "1. Don't be a dick"
+ "\n2. Follow rule number 1" + "\n2. Follow rule number 1"
+ "\n3. Follow Discord TOS" + "\n3. Follow Discord TOS"
+ "\n4. Do **not** post NSFW content, shock content, suggestive content" + "\n4. Do **not** post NSFW content, shock content, suggestive content"
+ "\n5. Please keep <#176333891320283136> professional and helpful, no random off-topic joking" + "\n5. Please keep <#176333891320283136> professional and helpful, no random off-topic joking"
+ "\nThis list will probably change in the future" + "\nThis list will probably change in the future"
}, data.guild.general, {}, { }, data.guild.general, {}, {
api: { api: {
getEffectivePower: mockGetEffectivePower(), getEffectivePower: mockGetEffectivePower(),
@ -1730,11 +1732,11 @@ test("message2event: channel links are converted even inside lists (parser post-
{ {
$type: "m.room.message", $type: "m.room.message",
body: "1. Don't be a dick" body: "1. Don't be a dick"
+ "\n2. Follow rule number 1" + "\n2. Follow rule number 1"
+ "\n3. Follow Discord TOS" + "\n3. Follow Discord TOS"
+ "\n4. Do **not** post NSFW content, shock content, suggestive content" + "\n4. Do **not** post NSFW content, shock content, suggestive content"
+ "\n5. Please keep #wonderland professional and helpful, no random off-topic joking" + "\n5. Please keep #wonderland professional and helpful, no random off-topic joking"
+ "\nThis list will probably change in the future", + "\nThis list will probably change in the future",
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: "<ol start=\"1\"><li>Don't be a dick</li><li>Follow rule number 1</li><li>Follow Discord TOS</li><li>Do <strong>not</strong> post NSFW content, shock content, suggestive content</li><li>Please keep <a href=\"https://matrix.to/#/!qzDBLKlildpzrrOnFZ:cadence.moe?via=cadence.moe&via=federated.nexus\">#wonderland</a> professional and helpful, no random off-topic joking</li></ol>This list will probably change in the future", formatted_body: "<ol start=\"1\"><li>Don't be a dick</li><li>Follow rule number 1</li><li>Follow Discord TOS</li><li>Do <strong>not</strong> post NSFW content, shock content, suggestive content</li><li>Please keep <a href=\"https://matrix.to/#/!qzDBLKlildpzrrOnFZ:cadence.moe?via=cadence.moe&via=federated.nexus\">#wonderland</a> professional and helpful, no random off-topic joking</li></ol>This list will probably change in the future",
"m.mentions": {}, "m.mentions": {},
@ -1783,31 +1785,33 @@ test("message2event: cross-room reply", async t => {
} }
}, },
content: "cross-room reply" content: "cross-room reply"
}, {}, {}, {api: { }, {}, {}, {
async getEvent(roomID, eventID) { api: {
called++ async getEvent(roomID, eventID) {
t.equal(roomID, "!mHmhQQPwXNananaOLD:cadence.moe") called++
t.equal(eventID, "$pgzCQjq_y5sy8RvWOUuoF3obNHjs8iNvt9c-odrOCPY") t.equal(roomID, "!mHmhQQPwXNananaOLD:cadence.moe")
return { t.equal(eventID, "$pgzCQjq_y5sy8RvWOUuoF3obNHjs8iNvt9c-odrOCPY")
type: "m.room.message", return {
sender: "@cadence:cadence.moe", type: "m.room.message",
content: { sender: "@cadence:cadence.moe",
"body": "image.png", content: {
"info": { "body": "image.png",
"h": 738, "info": {
"mimetype": "image/png", "h": 738,
"org.matrix.msc4230.is_animated": false, "mimetype": "image/png",
"size": 111189, "org.matrix.msc4230.is_animated": false,
"w": 772, "size": 111189,
"xyz.amorgan.blurhash": "L255Oa~qRPD$-pxuoJoLIUM{xuxu" "w": 772,
}, "xyz.amorgan.blurhash": "L255Oa~qRPD$-pxuoJoLIUM{xuxu"
"m.mentions": {}, },
"msgtype": "m.image", "m.mentions": {},
"url": "mxc://matrix.org/QbSujQjRLekzPknKlPsXbGDS" "msgtype": "m.image",
"url": "mxc://matrix.org/QbSujQjRLekzPknKlPsXbGDS"
}
} }
} }
} }
}}) })
t.deepEqual(events, [ t.deepEqual(events, [
{ {
$type: "m.room.message", $type: "m.room.message",
@ -1911,15 +1915,15 @@ test("message2event: single-choice poll", async t => {
kind: "org.matrix.msc3381.poll.disclosed", // Discord always lets you see results, so keeping this consistent with that. kind: "org.matrix.msc3381.poll.disclosed", // Discord always lets you see results, so keeping this consistent with that.
max_selections: 1, max_selections: 1,
answers: [{ answers: [{
id: "1", id: "1",
"org.matrix.msc1767.text": "[\ud83d\udc4d] answer one" "org.matrix.msc1767.text": "[\ud83d\udc4d] answer one"
}, { }, {
id: "2", id: "2",
"org.matrix.msc1767.text": "[\ud83d\udc4e] answer two" "org.matrix.msc1767.text": "[\ud83d\udc4e] answer two"
}, { }, {
id: "3", id: "3",
"org.matrix.msc1767.text": "answer three" "org.matrix.msc1767.text": "answer three"
}] }]
}, },
"org.matrix.msc1767.text": "only one answer allowed!\n1. [\ud83d\udc4d] answer one\n2. [\ud83d\udc4e] answer two\n3. answer three" "org.matrix.msc1767.text": "only one answer allowed!\n1. [\ud83d\udc4d] answer one\n2. [\ud83d\udc4e] answer two\n3. answer three"
}]) }])
@ -1938,15 +1942,15 @@ test("message2event: multiple-choice poll", async t => {
kind: "org.matrix.msc3381.poll.disclosed", // Discord always lets you see results, so keeping this consistent with that. kind: "org.matrix.msc3381.poll.disclosed", // Discord always lets you see results, so keeping this consistent with that.
max_selections: 3, max_selections: 3,
answers: [{ answers: [{
id: "1", id: "1",
"org.matrix.msc1767.text": "[😭] no" "org.matrix.msc1767.text": "[😭] no"
}, { }, {
id: "2", id: "2",
"org.matrix.msc1767.text": "oh no" "org.matrix.msc1767.text": "oh no"
}, { }, {
id: "3", id: "3",
"org.matrix.msc1767.text": "oh noooooo" "org.matrix.msc1767.text": "oh noooooo"
}] }]
}, },
"org.matrix.msc1767.text": "more than one answer allowed\n1. [😭] no\n2. oh no\n3. oh noooooo" "org.matrix.msc1767.text": "more than one answer allowed\n1. [😭] no\n2. oh no\n3. oh noooooo"
}]) }])

View file

@ -136,15 +136,17 @@ function timestampToSnowflakeInexact(timestamp) {
/** /**
* @param {string} url * @param {string} url
* @param {""|"?spoiler"} spoiler * @param {boolean} spoiler
*/ */
function getPublicUrlForCdn(url, spoiler="") { function getPublicUrlForCdn(url, spoiler=false) {
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]}${spoiler}` const transformedUrl = new URL(`${reg.ooye.bridge_origin}/download/discord${match[1]}/${match[2]}/${match[3]}/${match[4]}`)
if (spoiler) transformedUrl.searchParams.set("spoiler", "")
return transformedUrl.toString()
} }
/** /**

View file

@ -21,7 +21,7 @@ const schema = {
file_name: z.string().regex(/^[-A-Za-z0-9_.,]+$/) file_name: z.string().regex(/^[-A-Za-z0-9_.,]+$/)
}), }),
query: z.object({ query: z.object({
spoiler: z.optional(z.string().max(0)) // Lets you choose between no query param, and a ?spoiler query param (H3 considers it an empty string). Booleans do NOT work here, unfortunately. spoiler: z.string().optional()
}) })
} }