Compare commits
No commits in common. "90fcbd0ddc407434cf448ada14cc4d53d5d51b68" and "014a87ed9e74d939d6400b1833870e4dbed50bf6" have entirely different histories.
90fcbd0ddc
...
014a87ed9e
14 changed files with 179 additions and 288 deletions
64
package-lock.json
generated
64
package-lock.json
generated
|
|
@ -23,7 +23,7 @@
|
||||||
"ansi-colors": "^4.1.3",
|
"ansi-colors": "^4.1.3",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"chunk-text": "^2.0.1",
|
"chunk-text": "^2.0.1",
|
||||||
"cloudstorm": "^0.15.2",
|
"cloudstorm": "^0.14.0",
|
||||||
"discord-api-types": "^0.38.36",
|
"discord-api-types": "^0.38.36",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"lru-cache": "^11.0.2",
|
"lru-cache": "^11.0.2",
|
||||||
"prettier-bytes": "^1.0.4",
|
"prettier-bytes": "^1.0.4",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"snowtransfer": "^0.17.0",
|
"snowtransfer": "^0.14.2",
|
||||||
"stream-mime-type": "^1.0.2",
|
"stream-mime-type": "^1.0.2",
|
||||||
"try-to-catch": "^3.0.1",
|
"try-to-catch": "^3.0.1",
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
|
|
@ -1552,18 +1552,30 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cloudstorm": {
|
"node_modules/cloudstorm": {
|
||||||
"version": "0.15.2",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.14.1.tgz",
|
||||||
"integrity": "sha512-5y7E0uI39R3d7c+AWksqAQAlZlpx+qNjxjQfNIem2hh68s6QRmOFHTKu34I7pBE6JonpZf8AmoMYArY/4lLVmg==",
|
"integrity": "sha512-x95WCKg818E1rE1Ru45NPD3RoIq0pg3WxwvF0GE7Eq07pAeLcjSRqM1lUmbmfjdOqZrWdSRYA1NETVZ8QhVrIA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord-api-types": "^0.38.37",
|
"discord-api-types": "^0.38.21",
|
||||||
"snowtransfer": "^0.17.0"
|
"snowtransfer": "^0.15.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0"
|
"node": ">=22.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cloudstorm/node_modules/snowtransfer": {
|
||||||
|
"version": "0.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.15.0.tgz",
|
||||||
|
"integrity": "sha512-kEDGKtFiH5nSkHsDZonEUuDx99lUasJoZ7AGrgvE8HzVG59vjvqc//C+pjWj4DuJqTj4Q+Z1L/M/MYNim8F2VA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"discord-api-types": "^0.38.21"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.15.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
|
@ -1699,9 +1711,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/discord-api-types": {
|
"node_modules/discord-api-types": {
|
||||||
"version": "0.38.37",
|
"version": "0.38.36",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.37.tgz",
|
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.36.tgz",
|
||||||
"integrity": "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==",
|
"integrity": "sha512-qrbUbjjwtyeBg5HsAlm1C859epfOyiLjPqAOzkdWlCNsZCWJrertnETF/NwM8H+waMFU58xGSc5eXUfXah+WTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"scripts/actions/documentation"
|
"scripts/actions/documentation"
|
||||||
|
|
@ -1959,9 +1971,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/h3": {
|
"node_modules/h3": {
|
||||||
"version": "1.15.5",
|
"version": "1.15.4",
|
||||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
|
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz",
|
||||||
"integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
|
"integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie-es": "^1.2.2",
|
"cookie-es": "^1.2.2",
|
||||||
|
|
@ -1969,9 +1981,9 @@
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.5",
|
"destr": "^2.0.5",
|
||||||
"iron-webcrypto": "^1.2.1",
|
"iron-webcrypto": "^1.2.1",
|
||||||
"node-mock-http": "^1.0.4",
|
"node-mock-http": "^1.0.2",
|
||||||
"radix3": "^1.1.2",
|
"radix3": "^1.1.2",
|
||||||
"ufo": "^1.6.3",
|
"ufo": "^1.6.1",
|
||||||
"uncrypto": "^0.1.3"
|
"uncrypto": "^0.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -2309,9 +2321,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-mock-http": {
|
"node_modules/node-mock-http": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.2.tgz",
|
||||||
"integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==",
|
"integrity": "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
|
|
@ -2809,15 +2821,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/snowtransfer": {
|
"node_modules/snowtransfer": {
|
||||||
"version": "0.17.0",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.14.2.tgz",
|
||||||
"integrity": "sha512-H6Avpsco+HlVIkN+MbX34Q7+9g9Wci0wZQwGsvfw20VqEb7jnnk73iUcWytNMYtKZ72Ud58n6cFnQ3apTEamxw==",
|
"integrity": "sha512-Fi8OdRmaIgeCj58oVej+tQAoY2I+Xp/6PAYV8X93jE/2E6Anc87SbTbDV6WZXCnuzTQz3gty8JOGz02qI7Qs9A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord-api-types": "^0.38.37"
|
"discord-api-types": "^0.38.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0"
|
"node": ">=16.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
|
|
@ -3232,9 +3244,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.6.3",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
|
||||||
"integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
|
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/uncrypto": {
|
"node_modules/uncrypto": {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"ansi-colors": "^4.1.3",
|
"ansi-colors": "^4.1.3",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"chunk-text": "^2.0.1",
|
"chunk-text": "^2.0.1",
|
||||||
"cloudstorm": "^0.15.2",
|
"cloudstorm": "^0.14.0",
|
||||||
"discord-api-types": "^0.38.36",
|
"discord-api-types": "^0.38.36",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"lru-cache": "^11.0.2",
|
"lru-cache": "^11.0.2",
|
||||||
"prettier-bytes": "^1.0.4",
|
"prettier-bytes": "^1.0.4",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"snowtransfer": "^0.17.0",
|
"snowtransfer": "^0.14.2",
|
||||||
"stream-mime-type": "^1.0.2",
|
"stream-mime-type": "^1.0.2",
|
||||||
"try-to-catch": "^3.0.1",
|
"try-to-catch": "^3.0.1",
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
|
|
@ -64,6 +64,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --enable-source-maps start.js",
|
"start": "node --enable-source-maps start.js",
|
||||||
"setup": "node --enable-source-maps scripts/setup.js",
|
"setup": "node --enable-source-maps scripts/setup.js",
|
||||||
|
"build": "mkdir -p dist/out-of-your-element && cp -R src dist/out-of-your-element && cp -R docs dist/out-of-your-element && npx tsdown",
|
||||||
"addbot": "node addbot.js",
|
"addbot": "node addbot.js",
|
||||||
"test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js | tap-dot",
|
"test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js | tap-dot",
|
||||||
"test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js -- --slow | tap-dot",
|
"test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js -- --slow | tap-dot",
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ async function updatePins(channelID, roomID, convertedTimestamp) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
const kstate = await ks.roomToKState(roomID, [["m.room.pinned_events", ""]])
|
const kstate = await ks.roomToKState(roomID)
|
||||||
const pinned = pinsToList.pinsToList(discordPins, kstate)
|
const pinned = pinsToList.pinsToList(discordPins, kstate)
|
||||||
|
|
||||||
const diff = ks.diffKState(kstate, {"m.room.pinned_events/": {pinned}})
|
const diff = ks.diffKState(kstate, {"m.room.pinned_events/": {pinned}})
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,12 @@ test("edit2changes: edit of reply to skull webp attachment with content", async
|
||||||
newContent: {
|
newContent: {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "* Edit",
|
body: "> Extremity: Image\n\n* Edit",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body:
|
||||||
|
'<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$oLyUTyZ_7e_SUzGNWZKz880ll9amLZvXGbArJCKai2Q">In reply to</a> Extremity'
|
||||||
|
+ '<br>Image</blockquote></mx-reply>'
|
||||||
|
+ '* Edit',
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
"m.new_content": {
|
"m.new_content": {
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ function getDiscordParseCallbacks(message, guild, useHTML, spoilers = []) {
|
||||||
const username = message.mentions?.find(ment => ment.id === node.id)?.username
|
const username = message.mentions?.find(ment => ment.id === node.id)?.username
|
||||||
|| message.referenced_message?.mentions?.find(ment => ment.id === node.id)?.username
|
|| message.referenced_message?.mentions?.find(ment => ment.id === node.id)?.username
|
||||||
|| (interaction?.user.id === node.id ? interaction.user.username : null)
|
|| (interaction?.user.id === node.id ? interaction.user.username : null)
|
||||||
|| (message.author?.id === node.id ? message.author.username : null)
|
|| (message.author.id === node.id ? message.author.username : null)
|
||||||
|| "unknown-user"
|
|| node.id
|
||||||
if (mxid && useHTML) {
|
if (mxid && useHTML) {
|
||||||
return `<a href="https://matrix.to/#/${mxid}">@${username}</a>`
|
return `<a href="https://matrix.to/#/${mxid}">@${username}</a>`
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -480,61 +480,53 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback body/formatted_body for replies
|
// Fallback body/formatted_body for replies
|
||||||
// Generate a fallback if native replies are unsupported, which is in the following situations:
|
|
||||||
// 1. The replied-to event is in a different room to where the reply will be sent (i.e. a room upgrade occurred between)
|
|
||||||
// 2. The replied-to message has no corresponding Matrix event (repliedToUnknownEvent is true)
|
|
||||||
// This branch is optional - do NOT change anything apart from the reply fallback, since it may not be run
|
// This branch is optional - do NOT change anything apart from the reply fallback, since it may not be run
|
||||||
if ((repliedToEventRow || repliedToUnknownEvent) && options.includeReplyFallback !== false) {
|
if ((repliedToEventRow || repliedToUnknownEvent) && options.includeReplyFallback !== false) {
|
||||||
const latestRoomID = repliedToEventRow ? select("channel_room", "room_id", {channel_id: repliedToEventRow.channel_id}).pluck().get() : null
|
let repliedToDisplayName
|
||||||
if (latestRoomID !== repliedToEventRow?.room_id) repliedToEventInDifferentRoom = true
|
let repliedToUserHtml
|
||||||
|
if (repliedToEventRow?.source === 0 && repliedToEventSenderMxid) {
|
||||||
// check that condition 1 or 2 is met
|
const match = repliedToEventSenderMxid.match(/^@([^:]*)/)
|
||||||
if (repliedToEventInDifferentRoom || repliedToUnknownEvent) {
|
assert(match)
|
||||||
let referenced = message.referenced_message
|
repliedToDisplayName = message.referenced_message?.author.username || match[1] || "a Matrix user" // grab the localpart as the display name, whatever
|
||||||
if (!referenced) { // backend couldn't be bothered to dereference the message, have to do it ourselves
|
repliedToUserHtml = `<a href="https://matrix.to/#/${repliedToEventSenderMxid}">${repliedToDisplayName}</a>`
|
||||||
referenced = await discord.snow.channel.getChannelMessage(message.message_reference.channel_id, message.message_reference.message_id)
|
} else {
|
||||||
}
|
repliedToDisplayName = message.referenced_message?.author.global_name || message.referenced_message?.author.username || "a Discord user"
|
||||||
|
repliedToUserHtml = repliedToDisplayName
|
||||||
// Username
|
}
|
||||||
let repliedToDisplayName
|
let repliedToContent = message.referenced_message?.content
|
||||||
let repliedToUserHtml
|
if (repliedToContent?.match(/^(-# )?> (-# )?<:L1:/)) {
|
||||||
if (repliedToEventRow?.source === 0 && repliedToEventSenderMxid) {
|
// If the Discord user is replying to a Matrix user's reply, the fallback is going to contain the emojis and stuff from the bridged rep of the Matrix user's reply quote.
|
||||||
const match = repliedToEventSenderMxid.match(/^@([^:]*)/)
|
// Need to remove that previous reply rep from this fallback body. The fallbody body should only contain the Matrix user's actual message.
|
||||||
assert(match)
|
// ┌──────A─────┐ A reply rep starting with >quote or -#smalltext >quote. Match until the end of the line.
|
||||||
repliedToDisplayName = referenced.author.username || match[1] || "a Matrix user" // grab the localpart as the display name, whatever
|
// ┆ ┆┌─B─┐ There may be up to 2 reply rep lines in a row if it was created in the old format. Match all lines.
|
||||||
repliedToUserHtml = `<a href="https://matrix.to/#/${repliedToEventSenderMxid}">${repliedToDisplayName}</a>`
|
repliedToContent = repliedToContent.replace(/^((-# )?> .*\n){1,2}/, "")
|
||||||
} else {
|
}
|
||||||
repliedToDisplayName = referenced.author.global_name || referenced.author.username || "a Discord user"
|
if (repliedToContent == "") repliedToContent = "[Media]"
|
||||||
repliedToUserHtml = repliedToDisplayName
|
else if (!repliedToContent) repliedToContent = "[Replied-to message content wasn't provided by Discord]"
|
||||||
}
|
const {body: repliedToBody, html: repliedToHtml} = await transformContent(repliedToContent)
|
||||||
|
if (repliedToEventRow) {
|
||||||
// Content
|
// Generate a reply pointing to the Matrix event we found
|
||||||
let repliedToContent = referenced.content
|
const latestRoomID = select("channel_room", "room_id", {channel_id: repliedToEventRow.channel_id}).pluck().get() // native replies don't work across room upgrades, so make sure the old and new message are in the same room
|
||||||
if (repliedToContent?.match(/^(-# )?> (-# )?<:L1:/)) {
|
if (latestRoomID !== repliedToEventRow.room_id) repliedToEventInDifferentRoom = true
|
||||||
// If the Discord user is replying to a Matrix user's reply, the fallback is going to contain the emojis and stuff from the bridged rep of the Matrix user's reply quote.
|
html =
|
||||||
// Need to remove that previous reply rep from this fallback body. The fallbody body should only contain the Matrix user's actual message.
|
(latestRoomID === repliedToEventRow.room_id ? "<mx-reply>" : "")
|
||||||
// ┌──────A─────┐ A reply rep starting with >quote or -#smalltext >quote. Match until the end of the line.
|
+ `<blockquote><a href="https://matrix.to/#/${repliedToEventRow.room_id}/${repliedToEventRow.event_id}">In reply to</a> ${repliedToUserHtml}`
|
||||||
// ┆ ┆┌─B─┐ There may be up to 2 reply rep lines in a row if it was created in the old format. Match all lines.
|
+ `<br>${repliedToHtml}</blockquote>`
|
||||||
repliedToContent = repliedToContent.replace(/^((-# )?> .*\n){1,2}/, "")
|
+ (latestRoomID === repliedToEventRow.room_id ? "</mx-reply>" : "")
|
||||||
}
|
+ html
|
||||||
if (repliedToContent == "") repliedToContent = "[Media]"
|
body = (`${repliedToDisplayName}: ` // scenario 1 part B for mentions
|
||||||
const {body: repliedToBody, html: repliedToHtml} = await transformContent(repliedToContent)
|
+ repliedToBody).split("\n").map(line => "> " + line).join("\n")
|
||||||
|
+ "\n\n" + body
|
||||||
// Now branch on condition 1 or 2 for a different kind of fallback
|
} else { // repliedToUnknownEvent
|
||||||
if (repliedToEventRow) {
|
// This reply can't point to the Matrix event because it isn't bridged, we need to indicate this.
|
||||||
html = `<blockquote><a href="https://matrix.to/#/${repliedToEventRow.room_id}/${repliedToEventRow.event_id}">In reply to</a> ${repliedToUserHtml}`
|
assert(message.referenced_message)
|
||||||
+ `<br>${repliedToHtml}</blockquote>`
|
const dateDisplay = dUtils.howOldUnbridgedMessage(message.referenced_message.timestamp, message.timestamp)
|
||||||
+ html
|
html = `<blockquote>In reply to ${dateDisplay} from ${repliedToDisplayName}:`
|
||||||
body = `${repliedToDisplayName}: ${repliedToBody}`.split("\n").map(line => "> " + line).join("\n") // scenario 1 part B for mentions
|
+ `<br>${repliedToHtml}</blockquote>`
|
||||||
+ "\n\n" + body
|
+ html
|
||||||
} else { // repliedToUnknownEvent
|
body = (`In reply to ${dateDisplay}:\n${repliedToDisplayName}: `
|
||||||
const dateDisplay = dUtils.howOldUnbridgedMessage(referenced.timestamp, message.timestamp)
|
+ repliedToBody).split("\n").map(line => "> " + line).join("\n")
|
||||||
html = `<blockquote>In reply to ${dateDisplay} from ${repliedToDisplayName}:`
|
+ "\n\n" + body
|
||||||
+ `<br>${repliedToHtml}</blockquote>`
|
|
||||||
+ html
|
|
||||||
body = `In reply to ${dateDisplay}:\n${repliedToDisplayName}: ${repliedToBody}`.split("\n").map(line => "> " + line).join("\n")
|
|
||||||
+ "\n\n" + body
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -542,9 +534,16 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": mentions,
|
"m.mentions": mentions,
|
||||||
msgtype,
|
msgtype,
|
||||||
body: body,
|
body: body
|
||||||
format: "org.matrix.custom.html",
|
}
|
||||||
formatted_body: html
|
|
||||||
|
const isPlaintext = body === html
|
||||||
|
|
||||||
|
if (!isPlaintext || options.alwaysReturnFormattedBody) {
|
||||||
|
Object.assign(newTextMessageEvent, {
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: html
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
events.push(newTextMessageEvent)
|
events.push(newTextMessageEvent)
|
||||||
|
|
@ -603,6 +602,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
// Indent
|
// Indent
|
||||||
for (const event of forwardedEvents) {
|
for (const event of forwardedEvents) {
|
||||||
if (["m.text", "m.notice"].includes(event.msgtype)) {
|
if (["m.text", "m.notice"].includes(event.msgtype)) {
|
||||||
|
event.msgtype = "m.notice"
|
||||||
event.body = event.body.split("\n").map(l => "» " + l).join("\n")
|
event.body = event.body.split("\n").map(l => "» " + l).join("\n")
|
||||||
event.formatted_body = `<blockquote>${event.formatted_body}</blockquote>`
|
event.formatted_body = `<blockquote>${event.formatted_body}</blockquote>`
|
||||||
}
|
}
|
||||||
|
|
@ -688,18 +688,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
// Then attachments
|
// Then attachments
|
||||||
if (message.attachments) {
|
if (message.attachments) {
|
||||||
const attachmentEvents = await Promise.all(message.attachments.map(attachmentToEvent.bind(null, mentions)))
|
const attachmentEvents = await Promise.all(message.attachments.map(attachmentToEvent.bind(null, mentions)))
|
||||||
|
events.push(...attachmentEvents)
|
||||||
// Try to merge attachment events with the previous event
|
|
||||||
// This means that if the attachments ended up as a text link, and especially if there were many of them, the events will be joined together.
|
|
||||||
let prev = events.at(-1)
|
|
||||||
for (const atch of attachmentEvents) {
|
|
||||||
if (atch.msgtype === "m.text" && prev?.body && prev?.formatted_body && ["m.text", "m.notice"].includes(prev?.msgtype)) {
|
|
||||||
prev.body = prev.body + "\n" + atch.body
|
|
||||||
prev.formatted_body = prev.formatted_body + "<br>" + atch.formatted_body
|
|
||||||
} else {
|
|
||||||
events.push(atch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then embeds
|
// Then embeds
|
||||||
|
|
@ -833,16 +822,6 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip formatted_body where equivalent to body
|
|
||||||
if (!options.alwaysReturnFormattedBody) {
|
|
||||||
for (const event of events) {
|
|
||||||
if (["m.text", "m.notice"].includes(event.msgtype) && event.body === event.formatted_body) {
|
|
||||||
delete event.format
|
|
||||||
delete event.formatted_body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,11 @@ test("message2event: pk reply to matrix is converted to native matrix reply", as
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "this is a reply",
|
body: "> cadence [they]: now for my next experiment:\n\nthis is a reply",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: '<mx-reply><blockquote><a href="https://matrix.to/#/!TqlyQmifxGUggEmdBN:cadence.moe/$NB6nPgO2tfXyIwwDSF0Ga0BUrsgX1S-0Xl-jAvI8ucU">In reply to</a> <a href="https://matrix.to/#/@cadence:cadence.moe">cadence [they]</a><br>'
|
||||||
|
+ "now for my next experiment:</blockquote></mx-reply>"
|
||||||
|
+ "this is a reply",
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
event_id: "$NB6nPgO2tfXyIwwDSF0Ga0BUrsgX1S-0Xl-jAvI8ucU"
|
event_id: "$NB6nPgO2tfXyIwwDSF0Ga0BUrsgX1S-0Xl-jAvI8ucU"
|
||||||
|
|
@ -76,7 +80,11 @@ test("message2event: pk reply to discord is converted to native matrix reply", a
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
body: "this is a reply",
|
body: "> wing: some text\n\nthis is a reply",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: '<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA">In reply to</a> wing<br>'
|
||||||
|
+ "some text</blockquote></mx-reply>"
|
||||||
|
+ "this is a reply",
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
event_id: "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA"
|
event_id: "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA"
|
||||||
|
|
@ -112,7 +120,11 @@ test("message2event: pk reply to matrix attachment is converted to native matrix
|
||||||
"m.mentions": {
|
"m.mentions": {
|
||||||
user_ids: ["@ampflower:matrix.org"]
|
user_ids: ["@ampflower:matrix.org"]
|
||||||
},
|
},
|
||||||
body: "Cat nod",
|
body: "> Ampflower 🌺: [Media]\n\nCat nod",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: '<mx-reply><blockquote><a href="https://matrix.to/#/!TqlyQmifxGUggEmdBN:cadence.moe/$OEEK-Wam2FTh6J-6kVnnJ6KnLA_lLRnLTHatKKL62-Y">In reply to</a> <a href="https://matrix.to/#/@ampflower:matrix.org">Ampflower 🌺</a><br>'
|
||||||
|
+ "[Media]</blockquote></mx-reply>"
|
||||||
|
+ "Cat nod",
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
event_id: "$OEEK-Wam2FTh6J-6kVnnJ6KnLA_lLRnLTHatKKL62-Y"
|
event_id: "$OEEK-Wam2FTh6J-6kVnnJ6KnLA_lLRnLTHatKKL62-Y"
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,12 @@ test("message2event: reply to skull webp attachment with content", async t => {
|
||||||
},
|
},
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "Reply"
|
body: "> Extremity: Image\n\nReply",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body:
|
||||||
|
'<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$oLyUTyZ_7e_SUzGNWZKz880ll9amLZvXGbArJCKai2Q">In reply to</a> Extremity'
|
||||||
|
+ '<br>Image</blockquote></mx-reply>'
|
||||||
|
+ 'Reply'
|
||||||
}, {
|
}, {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
|
|
@ -467,7 +472,12 @@ test("message2event: simple reply to matrix user", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "Reply"
|
body: "> cadence: so can you reply to my webhook uwu\n\nReply",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body:
|
||||||
|
'<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4">In reply to</a> <a href="https://matrix.to/#/@cadence:cadence.moe">cadence</a>'
|
||||||
|
+ '<br>so can you reply to my webhook uwu</blockquote></mx-reply>'
|
||||||
|
+ 'Reply'
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -529,7 +539,12 @@ test("message2event: reply to matrix user with mention", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "kys"
|
body: "> okay 🤍 yay 🤍: @extremity: you owe me $30\n\nkys",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body:
|
||||||
|
'<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$7P2O_VTQNHvavX5zNJ35DV-dbJB1Ag80tGQP_JzGdhk">In reply to</a> <a href="https://matrix.to/#/@cadence:cadence.moe">okay 🤍 yay 🤍</a>'
|
||||||
|
+ '<br><a href="https://matrix.to/#/@_ooye_extremity:cadence.moe">@extremity</a> you owe me $30</blockquote></mx-reply>'
|
||||||
|
+ 'kys'
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -641,7 +656,9 @@ 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: "> cadence [they]: What about them?\n\nWell, they don't seem to...",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "<mx-reply><blockquote><a href=\"https://matrix.to/#/!FuDZhlOAtqswlyxzeR:cadence.moe/$nUM-ABBF8KdnvrhXwLlYAE9dgDl_tskOvvcNIBrtsVo\">In reply to</a> <a href=\"https://matrix.to/#/@cadence:cadence.moe\">cadence [they]</a><br>What about them?</blockquote></mx-reply>Well, they don't seem to...",
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -678,7 +695,9 @@ 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: `> Ami (she/her): let me guess they got a lot of bug reports like "empty chest with no loot?"\n\nMost likely`,
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: `<mx-reply><blockquote><a href="https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$W1nsDhNIojWrcQOdnOD9RaEvrz2qyZErQoNhPRs1nK4">In reply to</a> <a href="https://matrix.to/#/@ami:the-apothecary.club">Ami (she/her)</a><br>let me guess they got a lot of bug reports like "empty chest with no loot?"</blockquote></mx-reply>Most likely`,
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -715,7 +734,9 @@ 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: `> Ami (she/her): let me guess they got a lot of bug reports like "empty chest with no loot?"\n\nMost likely`,
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: `<mx-reply><blockquote><a href="https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$W1nsDhNIojWrcQOdnOD9RaEvrz2qyZErQoNhPRs1nK4">In reply to</a> <a href="https://matrix.to/#/@ami:the-apothecary.club">Ami (she/her)</a><br>let me guess they got a lot of bug reports like "empty chest with no loot?"</blockquote></mx-reply>Most likely`,
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -865,66 +886,18 @@ test("message2event: very large attachment is linked instead of being uploaded",
|
||||||
size: 100e6
|
size: 100e6
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
t.deepEqual(events, [{
|
|
||||||
$type: "m.room.message",
|
|
||||||
"m.mentions": {},
|
|
||||||
msgtype: "m.text",
|
|
||||||
body: "hey\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)",
|
|
||||||
format: "org.matrix.custom.html",
|
|
||||||
formatted_body: 'hey<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)'
|
|
||||||
}])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("message2event: multiple attachments are combined into the same event where possible", async t => {
|
|
||||||
const events = await messageToEvent({
|
|
||||||
content: "hey",
|
|
||||||
attachments: [{
|
|
||||||
filename: "hey.jpg",
|
|
||||||
url: "https://cdn.discordapp.com/attachments/123/456/789.mega",
|
|
||||||
content_type: "application/i-made-it-up",
|
|
||||||
size: 100e6
|
|
||||||
}, {
|
|
||||||
filename: "SPOILER_secret.jpg",
|
|
||||||
url: "https://cdn.discordapp.com/attachments/123/456/SPOILER_secret.jpg",
|
|
||||||
content_type: "image/jpeg",
|
|
||||||
size: 38291
|
|
||||||
}, {
|
|
||||||
filename: "my enemies.txt",
|
|
||||||
url: "https://cdn.discordapp.com/attachments/123/456/my_enemies.txt",
|
|
||||||
content_type: "text/plain",
|
|
||||||
size: 8911
|
|
||||||
}, {
|
|
||||||
filename: "hey.jpg",
|
|
||||||
url: "https://cdn.discordapp.com/attachments/123/456/789.mega",
|
|
||||||
content_type: "application/i-made-it-up",
|
|
||||||
size: 100e6
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
t.deepEqual(events, [{
|
t.deepEqual(events, [{
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
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 SPOILER file: https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg (38 KB)"
|
|
||||||
+ "\n📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)",
|
|
||||||
format: "org.matrix.custom.html",
|
|
||||||
formatted_body: "hey"
|
|
||||||
+ `<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 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": {},
|
||||||
msgtype: "m.file",
|
msgtype: "m.text",
|
||||||
body: "my enemies.txt",
|
body: "📄 Uploaded file: https://bridge.example.org/download/discordcdn/123/456/789.mega (100 MB)",
|
||||||
filename: "my enemies.txt",
|
format: "org.matrix.custom.html",
|
||||||
external_url: "https://bridge.example.org/download/discordcdn/123/456/my_enemies.txt",
|
formatted_body: '📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)'
|
||||||
url: "mxc://cadence.moe/y89EOTRp2lbeOkgdsEleGOge",
|
|
||||||
info: {
|
|
||||||
mimetype: "text/plain",
|
|
||||||
size: 8911
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -1138,7 +1111,7 @@ test("message2event: constructed forwarded message", async t => {
|
||||||
formatted_body: `🔀 <em>Forwarded from wonderland <a href="https://matrix.to/#/!qzDBLKlildpzrrOnFZ:cadence.moe/$tBIT8mO7XTTCgIINyiAIy6M2MSoPAdJenRl_RLyYuaE?via=cadence.moe&via=matrix.org">[jump to event]</a></em>`
|
formatted_body: `🔀 <em>Forwarded from wonderland <a href="https://matrix.to/#/!qzDBLKlildpzrrOnFZ:cadence.moe/$tBIT8mO7XTTCgIINyiAIy6M2MSoPAdJenRl_RLyYuaE?via=cadence.moe&via=matrix.org">[jump to event]</a></em>`
|
||||||
+ `<br><blockquote>What's cooking, good looking? <img data-mx-emoticon height="32" src="mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc" title=":hipposcope:" alt=":hipposcope:"></blockquote>`,
|
+ `<br><blockquote>What's cooking, good looking? <img data-mx-emoticon height="32" src="mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc" title=":hipposcope:" alt=":hipposcope:"></blockquote>`,
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
msgtype: "m.text",
|
msgtype: "m.notice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
|
|
@ -1197,7 +1170,7 @@ test("message2event: constructed forwarded text", async t => {
|
||||||
formatted_body: `🔀 <em>Forwarded from amanda-spam <a href="https://matrix.to/#/!CzvdIdUQXgUjDVKxeU:cadence.moe?via=cadence.moe&via=matrix.org">[jump to room]</a></em>`
|
formatted_body: `🔀 <em>Forwarded from amanda-spam <a href="https://matrix.to/#/!CzvdIdUQXgUjDVKxeU:cadence.moe?via=cadence.moe&via=matrix.org">[jump to room]</a></em>`
|
||||||
+ `<br><blockquote>What's cooking, good looking?</blockquote>`,
|
+ `<br><blockquote>What's cooking, good looking?</blockquote>`,
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
msgtype: "m.text",
|
msgtype: "m.notice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
|
|
@ -1220,7 +1193,7 @@ test("message2event: don't scan forwarded messages for mentions", async t => {
|
||||||
formatted_body: `🔀 <em>Forwarded message</em>`
|
formatted_body: `🔀 <em>Forwarded message</em>`
|
||||||
+ `<br><blockquote>If some folks have spare bandwidth then helping out ArchiveTeam with archiving soon to be deleted research and government data might be worthwhile <a href="https://social.luca.run/@luca/113950834185678114">https://social.luca.run/@luca/113950834185678114</a></blockquote>`,
|
+ `<br><blockquote>If some folks have spare bandwidth then helping out ArchiveTeam with archiving soon to be deleted research and government data might be worthwhile <a href="https://social.luca.run/@luca/113950834185678114">https://social.luca.run/@luca/113950834185678114</a></blockquote>`,
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
msgtype: "m.text"
|
msgtype: "m.notice"
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
@ -1477,77 +1450,3 @@ test("message2event: cross-room reply", async t => {
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test("message2event: forwarded message with unreferenced mention", async t => {
|
|
||||||
const events = await messageToEvent({
|
|
||||||
type: 0,
|
|
||||||
content: "",
|
|
||||||
attachments: [],
|
|
||||||
embeds: [],
|
|
||||||
timestamp: "2026-01-20T14:14:21.281Z",
|
|
||||||
edited_timestamp: null,
|
|
||||||
flags: 16384,
|
|
||||||
components: [],
|
|
||||||
id: "1463174818823405651",
|
|
||||||
channel_id: "893634327722721290",
|
|
||||||
author: {
|
|
||||||
id: "100031256988766208",
|
|
||||||
username: "leo60228",
|
|
||||||
discriminator: "0",
|
|
||||||
avatar: "8a164f29946f23eb4f45cde71a75e5a6",
|
|
||||||
avatar_decoration_data: null,
|
|
||||||
public_flags: 768,
|
|
||||||
global_name: "leo vriska",
|
|
||||||
primary_guild: null,
|
|
||||||
collectibles: null,
|
|
||||||
display_name_styles: null
|
|
||||||
},
|
|
||||||
bot: false,
|
|
||||||
pinned: false,
|
|
||||||
mentions: [],
|
|
||||||
mention_roles: [],
|
|
||||||
mention_everyone: false,
|
|
||||||
tts: false,
|
|
||||||
message_reference: {
|
|
||||||
type: 1,
|
|
||||||
channel_id: "937181373943382036",
|
|
||||||
message_id: "1032034158261846038",
|
|
||||||
guild_id: "936370934292549712"
|
|
||||||
},
|
|
||||||
message_snapshots: [
|
|
||||||
{
|
|
||||||
message: {
|
|
||||||
type: 0,
|
|
||||||
content: "<@77084495118868480>",
|
|
||||||
attachments: [
|
|
||||||
{
|
|
||||||
id: "1463174815119704114",
|
|
||||||
filename: "2022-10-18_16-49-46.mp4",
|
|
||||||
size: 51238885,
|
|
||||||
url: "https://cdn.discordapp.com/attachments/893634327722721290/1463174815119704114/2022-10-18_16-49-46.mp4?ex=6970df3c&is=696f8dbc&hm=515d3cbcc8464bdada7f4c3d9ccc8174f671cb75391ce21a46a804fcb1e4befe&",
|
|
||||||
proxy_url: "https://media.discordapp.net/attachments/893634327722721290/1463174815119704114/2022-10-18_16-49-46.mp4?ex=6970df3c&is=696f8dbc&hm=515d3cbcc8464bdada7f4c3d9ccc8174f671cb75391ce21a46a804fcb1e4befe&",
|
|
||||||
width: 1920,
|
|
||||||
height: 1080,
|
|
||||||
content_type: "video/mp4",
|
|
||||||
content_scan_version: 3,
|
|
||||||
spoiler: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
embeds: [],
|
|
||||||
timestamp: "2022-10-18T20:55:17.597Z",
|
|
||||||
edited_timestamp: null,
|
|
||||||
flags: 0,
|
|
||||||
components: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
t.deepEqual(events, [{
|
|
||||||
$type: "m.room.message",
|
|
||||||
msgtype: "m.text",
|
|
||||||
body: "[🔀 Forwarded message]\n» @unknown-user:\n» 🎞️ Uploaded file: https://bridge.example.org/download/discordcdn/893634327722721290/1463174815119704114/2022-10-18_16-49-46.mp4 (51 MB)",
|
|
||||||
format: "org.matrix.custom.html",
|
|
||||||
formatted_body: "🔀 <em>Forwarded message</em><br><blockquote>@unknown-user:<br>🎞️ Uploaded file: <a href=\"https://bridge.example.org/download/discordcdn/893634327722721290/1463174815119704114/2022-10-18_16-49-46.mp4\">2022-10-18_16-49-46.mp4</a> (51 MB)</blockquote>",
|
|
||||||
"m.mentions": {}
|
|
||||||
}])
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@
|
||||||
const {select} = require("../../passthrough")
|
const {select} = require("../../passthrough")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("discord-api-types/v10").RESTGetAPIChannelMessagesPinsResult} pins
|
* @param {import("discord-api-types/v10").RESTGetAPIChannelPinsResult} pins
|
||||||
* @param {{"m.room.pinned_events/"?: {pinned?: string[]}}} kstate
|
* @param {{"m.room.pinned_events/"?: {pinned?: string[]}}} kstate
|
||||||
*/
|
*/
|
||||||
function pinsToList(pins, kstate) {
|
function pinsToList(pins, kstate) {
|
||||||
/** Most recent last. */
|
|
||||||
let alreadyPinned = kstate["m.room.pinned_events/"]?.pinned || []
|
let alreadyPinned = kstate["m.room.pinned_events/"]?.pinned || []
|
||||||
|
|
||||||
// If any of the already pinned messages are bridged messages then remove them from the already pinned list.
|
// If any of the already pinned messages are bridged messages then remove them from the already pinned list.
|
||||||
|
|
@ -16,13 +15,13 @@ function pinsToList(pins, kstate) {
|
||||||
// * Matrix-only unbridged messages that are pinned will remain pinned.
|
// * Matrix-only unbridged messages that are pinned will remain pinned.
|
||||||
alreadyPinned = alreadyPinned.filter(event_id => {
|
alreadyPinned = alreadyPinned.filter(event_id => {
|
||||||
const messageID = select("event_message", "message_id", {event_id}).pluck().get()
|
const messageID = select("event_message", "message_id", {event_id}).pluck().get()
|
||||||
return !messageID || pins.items.find(m => m.message.id === messageID) // if it is bridged then remove it from the filter
|
return !messageID || pins.find(m => m.id === messageID) // if it is bridged then remove it from the filter
|
||||||
})
|
})
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const result = []
|
const result = []
|
||||||
for (const pin of pins.items) {
|
for (const message of pins) {
|
||||||
const eventID = select("event_message", "event_id", {message_id: pin.message.id, part: 0}).pluck().get()
|
const eventID = select("event_message", "event_id", {message_id: message.id, part: 0}).pluck().get()
|
||||||
if (eventID && !alreadyPinned.includes(eventID)) result.push(eventID)
|
if (eventID && !alreadyPinned.includes(eventID)) result.push(eventID)
|
||||||
}
|
}
|
||||||
result.reverse()
|
result.reverse()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const data = require("../../../test/data")
|
const data = require("../../../test/data")
|
||||||
const {pinsToList} = require("./pins-to-list")
|
const {pinsToList} = require("./pins-to-list")
|
||||||
const mixin = require("@cloudrac3r/mixin-deep")
|
|
||||||
|
|
||||||
test("pins2list: converts known IDs, ignores unknown IDs", t => {
|
test("pins2list: converts known IDs, ignores unknown IDs", t => {
|
||||||
const result = pinsToList(data.pins.faked, {})
|
const result = pinsToList(data.pins.faked, {})
|
||||||
|
|
@ -47,9 +46,7 @@ test("pins2list: already pinned unknown items are not moved", t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test("pins2list: bridged messages can be unpinned", t => {
|
test("pins2list: bridged messages can be unpinned", t => {
|
||||||
const shortPins = mixin({}, data.pins.faked)
|
const result = pinsToList(data.pins.faked.slice(0, -2), {
|
||||||
shortPins.items = shortPins.items.slice(0, -2)
|
|
||||||
const result = pinsToList(shortPins, {
|
|
||||||
"m.room.pinned_events/": {
|
"m.room.pinned_events/": {
|
||||||
pinned: [
|
pinned: [
|
||||||
"$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA",
|
"$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA",
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,14 @@ test("user2name: works on special user", t => {
|
||||||
t.equal(userToSimName(data.user.clyde_ai), "clyde_ai")
|
t.equal(userToSimName(data.user.clyde_ai), "clyde_ai")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("user2name: includes ID if requested in config", t => {
|
||||||
|
const {reg} = require("../../matrix/read-registration")
|
||||||
|
reg.ooye.include_user_id_in_mxid = true
|
||||||
|
t.equal(userToSimName({username: "Harry Styles!", discriminator: "0001", id: "123456"}), "123456_harry_styles")
|
||||||
|
t.equal(userToSimName({username: "f***", discriminator: "0001", id: "123456"}), "123456_f")
|
||||||
|
reg.ooye.include_user_id_in_mxid = false
|
||||||
|
})
|
||||||
|
|
||||||
test("webhook author: can generate sim names", t => {
|
test("webhook author: can generate sim names", t => {
|
||||||
t.equal(webhookAuthorToSimName({
|
t.equal(webhookAuthorToSimName({
|
||||||
username: "Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆",
|
username: "Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆",
|
||||||
|
|
@ -52,10 +60,3 @@ test("webhook author: can generate sim names", t => {
|
||||||
id: "123"
|
id: "123"
|
||||||
}), "webhook_cadence_maid_of_creation_eye_of_clarity_empress_of_hope")
|
}), "webhook_cadence_maid_of_creation_eye_of_clarity_empress_of_hope")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("user2name: includes ID if requested in config", t => {
|
|
||||||
const {reg} = require("../../matrix/read-registration")
|
|
||||||
reg.ooye.include_user_id_in_mxid = true
|
|
||||||
t.equal(userToSimName({username: "Harry Styles!", discriminator: "0001", id: "123456"}), "123456_harry_styles")
|
|
||||||
t.equal(userToSimName({username: "f***", discriminator: "0001", id: "123456"}), "123456_f")
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -140,20 +140,10 @@ function diffKState(actual, target) {
|
||||||
/**
|
/**
|
||||||
* Async because it gets all room state from the homeserver.
|
* Async because it gets all room state from the homeserver.
|
||||||
* @param {string} roomID
|
* @param {string} roomID
|
||||||
* @param {[type: string, key: string][]} [limitToEvents]
|
|
||||||
*/
|
*/
|
||||||
async function roomToKState(roomID, limitToEvents) {
|
async function roomToKState(roomID) {
|
||||||
if (!limitToEvents) {
|
const root = await api.getAllState(roomID)
|
||||||
const root = await api.getAllState(roomID)
|
return stateToKState(root)
|
||||||
return stateToKState(root)
|
|
||||||
} else {
|
|
||||||
const root = []
|
|
||||||
await Promise.all(limitToEvents.map(async ([type, key]) => {
|
|
||||||
const outer = await api.getStateEventOuter(roomID, type, key)
|
|
||||||
root.push(outer)
|
|
||||||
}))
|
|
||||||
return stateToKState(root)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
14
test/data.js
14
test/data.js
|
|
@ -1256,14 +1256,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pins: {
|
pins: {
|
||||||
faked: {
|
faked: [
|
||||||
items: [
|
{id: "1126786462646550579"},
|
||||||
{message: {id: "1126786462646550579"}},
|
{id: "1141501302736695316"},
|
||||||
{message: {id: "1141501302736695316"}},
|
{id: "1106366167788044450"},
|
||||||
{message: {id: "1106366167788044450"}},
|
{id: "1115688611186193400"}
|
||||||
{message: {id: "1115688611186193400"}}
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
// Display order is text content, attachments, then stickers
|
// Display order is text content, attachments, then stickers
|
||||||
|
|
|
||||||
|
|
@ -151,8 +151,7 @@ INSERT INTO file (discord_url, mxc_url) VALUES
|
||||||
('https://cdn.discordapp.com/attachments/112760669178241024/1197621094786531358/Ins_1960637570.mp4', 'mxc://cadence.moe/kMqLycqMURhVpwleWkmASpnU'),
|
('https://cdn.discordapp.com/attachments/112760669178241024/1197621094786531358/Ins_1960637570.mp4', 'mxc://cadence.moe/kMqLycqMURhVpwleWkmASpnU'),
|
||||||
('https://cdn.discordapp.com/attachments/1099031887500034088/1112476845502365786/voice-message.ogg', 'mxc://cadence.moe/MRRPDggXQMYkrUjTpxQbmcxB'),
|
('https://cdn.discordapp.com/attachments/1099031887500034088/1112476845502365786/voice-message.ogg', 'mxc://cadence.moe/MRRPDggXQMYkrUjTpxQbmcxB'),
|
||||||
('https://cdn.discordapp.com/attachments/122155380120748034/1174514575220158545/the.yml', 'mxc://cadence.moe/HnQIYQmmlIKwOQsbFsIGpzPP'),
|
('https://cdn.discordapp.com/attachments/122155380120748034/1174514575220158545/the.yml', 'mxc://cadence.moe/HnQIYQmmlIKwOQsbFsIGpzPP'),
|
||||||
('https://cdn.discordapp.com/attachments/112760669178241024/1296237494987133070/100km.gif', 'mxc://cadence.moe/qDAotmebTfEIfsAIVCEZptLh'),
|
('https://cdn.discordapp.com/attachments/112760669178241024/1296237494987133070/100km.gif', 'mxc://cadence.moe/qDAotmebTfEIfsAIVCEZptLh');
|
||||||
('https://cdn.discordapp.com/attachments/123/456/my_enemies.txt', 'mxc://cadence.moe/y89EOTRp2lbeOkgdsEleGOge');
|
|
||||||
|
|
||||||
INSERT INTO emoji (emoji_id, name, animated, mxc_url) VALUES
|
INSERT INTO emoji (emoji_id, name, animated, mxc_url) VALUES
|
||||||
('230201364309868544', 'hippo', 0, 'mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC'),
|
('230201364309868544', 'hippo', 0, 'mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC'),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ reg.ooye.bridge_origin = "https://bridge.example.org"
|
||||||
reg.ooye.time_zone = "Pacific/Auckland"
|
reg.ooye.time_zone = "Pacific/Auckland"
|
||||||
reg.ooye.max_file_size = 5000000
|
reg.ooye.max_file_size = 5000000
|
||||||
reg.ooye.web_password = "password123"
|
reg.ooye.web_password = "password123"
|
||||||
reg.ooye.include_user_id_in_mxid = false
|
|
||||||
|
|
||||||
const sync = new HeatSync({watchFS: false})
|
const sync = new HeatSync({watchFS: false})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue