Spoiler Fix #90
4 changed files with 122 additions and 116 deletions
Use less hacky way of setting the params
commit
15055b8ee0
|
|
@ -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/") ? "🖼️"
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
@ -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", {
|
||||||
|
headers: {
|
||||||
"Content-Type": "image/jpeg",
|
"Content-Type": "image/jpeg",
|
||||||
"Content-Length": 483085
|
"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",
|
||||||
|
|
@ -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() {
|
||||||
|
|
@ -1783,7 +1785,8 @@ test("message2event: cross-room reply", async t => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content: "cross-room reply"
|
content: "cross-room reply"
|
||||||
}, {}, {}, {api: {
|
}, {}, {}, {
|
||||||
|
api: {
|
||||||
async getEvent(roomID, eventID) {
|
async getEvent(roomID, eventID) {
|
||||||
called++
|
called++
|
||||||
t.equal(roomID, "!mHmhQQPwXNananaOLD:cadence.moe")
|
t.equal(roomID, "!mHmhQQPwXNananaOLD:cadence.moe")
|
||||||
|
|
@ -1807,7 +1810,8 @@ test("message2event: cross-room reply", async t => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}})
|
}
|
||||||
|
})
|
||||||
t.deepEqual(events, [
|
t.deepEqual(events, [
|
||||||
{
|
{
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue