Compare commits
No commits in common. "c8b0f23db32f7a4b916a1bdad876ac706076cdb8" and "3d3671e05a13d4a3a21006f35afe0ca50db1fcca" have entirely different histories.
c8b0f23db3
...
3d3671e05a
9 changed files with 84 additions and 156 deletions
37
package-lock.json
generated
37
package-lock.json
generated
|
|
@ -10,7 +10,7 @@
|
|||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.7",
|
||||
"@cloudrac3r/giframe": "^0.4.3",
|
||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||
|
|
@ -242,9 +242,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@cloudrac3r/discord-markdown": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.10.tgz",
|
||||
"integrity": "sha512-E+F9UYDUHP2kHDCciX63SBzgsUnHpu2Pp/h98x9Zo+vKuzXjCQ5PcFNdUlH6M18bvHDZPoIsKVmjnON8UYaAPQ==",
|
||||
"version": "2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.8.tgz",
|
||||
"integrity": "sha512-ZrSimHqmLqXR+W3U1n6ge6poAjmQaMzXyWrTkT36znrgKhfuQAYxLBtKTf7m+cmr3VlaDVM2P+iPdSeTeaM0qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"simple-markdown": "^0.7.3"
|
||||
|
|
@ -1227,15 +1227,26 @@
|
|||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
|
||||
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
|
||||
"license": "MIT",
|
||||
"version": "18.2.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz",
|
||||
"integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==",
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
|
|
@ -1657,10 +1668,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "2.0.2",
|
||||
|
|
@ -2815,7 +2825,6 @@
|
|||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.7.3.tgz",
|
||||
"integrity": "sha512-uGXIc13NGpqfPeFJIt/7SHHxd6HekEJYtsdoCM06mEBPL9fQH/pSD7LRM6PZ7CKchpSvxKL4tvwMamqAaNDAyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": ">=16.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.7",
|
||||
"@cloudrac3r/giframe": "^0.4.3",
|
||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||
|
|
@ -64,7 +64,6 @@
|
|||
"scripts": {
|
||||
"start": "node --enable-source-maps start.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",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -20,39 +20,31 @@ const emitter = new EventEmitter()
|
|||
* (or before the it has finished being bridged to an event).
|
||||
* In this case, wait until the original message has finished bridging, then retrigger the passed function.
|
||||
* @template {(...args: any[]) => Promise<any>} T
|
||||
* @param {string} inputID
|
||||
* @param {string} messageID
|
||||
* @param {T} fn
|
||||
* @param {Parameters<T>} rest
|
||||
* @returns {boolean} false if the event was found and the function will be ignored, true if the event was not found and the function will be retriggered
|
||||
*/
|
||||
function eventNotFoundThenRetrigger(inputID, fn, ...rest) {
|
||||
if (!paused.has(inputID)) {
|
||||
if (inputID.match(/^[0-9]+$/)) {
|
||||
const eventID = select("event_message", "event_id", {message_id: inputID}).pluck().get()
|
||||
function eventNotFoundThenRetrigger(messageID, fn, ...rest) {
|
||||
if (!paused.has(messageID)) {
|
||||
const eventID = select("event_message", "event_id", {message_id: messageID}).pluck().get()
|
||||
if (eventID) {
|
||||
debugRetrigger(`[retrigger] OK mid <-> eid = ${inputID} <-> ${eventID}`)
|
||||
debugRetrigger(`[retrigger] OK mid <-> eid = ${messageID} <-> ${eventID}`)
|
||||
return false // event was found so don't retrigger
|
||||
}
|
||||
} else if (inputID.match(/^\$/)) {
|
||||
const messageID = select("event_message", "message_id", {event_id: inputID}).pluck().get()
|
||||
if (messageID) {
|
||||
debugRetrigger(`[retrigger] OK eid <-> mid = ${inputID} <-> ${messageID}`)
|
||||
return false // message was found so don't retrigger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugRetrigger(`[retrigger] WAIT id = ${inputID}`)
|
||||
emitter.once(inputID, () => {
|
||||
debugRetrigger(`[retrigger] TRIGGER id = ${inputID}`)
|
||||
debugRetrigger(`[retrigger] WAIT mid = ${messageID}`)
|
||||
emitter.once(messageID, () => {
|
||||
debugRetrigger(`[retrigger] TRIGGER mid = ${messageID}`)
|
||||
fn(...rest)
|
||||
})
|
||||
// if the event never arrives, don't trigger the callback, just clean up
|
||||
setTimeout(() => {
|
||||
if (emitter.listeners(inputID).length) {
|
||||
debugRetrigger(`[retrigger] EXPIRE id = ${inputID}`)
|
||||
if (emitter.listeners(messageID).length) {
|
||||
debugRetrigger(`[retrigger] EXPIRE mid = ${messageID}`)
|
||||
}
|
||||
emitter.removeAllListeners(inputID)
|
||||
emitter.removeAllListeners(messageID)
|
||||
}, 60 * 1000) // 1 minute
|
||||
return true // event was not found, then retrigger
|
||||
}
|
||||
|
|
@ -66,11 +58,11 @@ function eventNotFoundThenRetrigger(inputID, fn, ...rest) {
|
|||
*/
|
||||
async function pauseChanges(messageID, promise) {
|
||||
try {
|
||||
debugRetrigger(`[retrigger] PAUSE id = ${messageID}`)
|
||||
debugRetrigger(`[retrigger] PAUSE mid = ${messageID}`)
|
||||
paused.add(messageID)
|
||||
return await promise
|
||||
} finally {
|
||||
debugRetrigger(`[retrigger] RESUME id = ${messageID}`)
|
||||
debugRetrigger(`[retrigger] RESUME mid = ${messageID}`)
|
||||
paused.delete(messageID)
|
||||
messageFinishedBridging(messageID)
|
||||
}
|
||||
|
|
@ -82,7 +74,7 @@ async function pauseChanges(messageID, promise) {
|
|||
*/
|
||||
function messageFinishedBridging(messageID) {
|
||||
if (emitter.listeners(messageID).length) {
|
||||
debugRetrigger(`[retrigger] EMIT id = ${messageID}`)
|
||||
debugRetrigger(`[retrigger] EMIT mid = ${messageID}`)
|
||||
}
|
||||
emitter.emit(messageID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,21 +312,6 @@ test("message2event embeds: youtube video", async t => {
|
|||
}])
|
||||
})
|
||||
|
||||
test("message2event embeds: embed not bridged if its link was spoilered", async t => {
|
||||
const events = await messageToEvent({
|
||||
...data.message_with_embeds.youtube_video,
|
||||
content: "||https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E\n\n\nJutomi I'm gonna make these sounds in your walls tonight||"
|
||||
}, data.guild.general)
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
msgtype: "m.text",
|
||||
body: "[spoiler]",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: `<span data-mx-spoiler=""><a href="https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E">https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E</a><br><br><br>Jutomi I'm gonna make these sounds in your walls tonight</span>`,
|
||||
"m.mentions": {}
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event embeds: tenor gif should show a video link without a provider", async t => {
|
||||
const events = await messageToEvent(data.message_with_embeds.tenor_gif, data.guild.general, {}, {})
|
||||
t.deepEqual(events, [{
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@ const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
|
|||
* @param {DiscordTypes.APIMessage} message
|
||||
* @param {DiscordTypes.APIGuild} guild
|
||||
* @param {boolean} useHTML
|
||||
* @param {string[]} spoilers
|
||||
*/
|
||||
function getDiscordParseCallbacks(message, guild, useHTML, spoilers = []) {
|
||||
function getDiscordParseCallbacks(message, guild, useHTML) {
|
||||
return {
|
||||
/** @param {{id: string, type: "discordUser"}} node */
|
||||
user: node => {
|
||||
|
|
@ -91,10 +90,6 @@ function getDiscordParseCallbacks(message, guild, useHTML, spoilers = []) {
|
|||
here: () => {
|
||||
if (message.mention_everyone) return "@room"
|
||||
return "@here"
|
||||
},
|
||||
spoiler: node => {
|
||||
spoilers.push(node.raw)
|
||||
return useHTML
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,8 +281,8 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
// Mentions scenarios 1 and 2, part A. i.e. translate relevant message.mentions to m.mentions
|
||||
// (Still need to do scenarios 1 and 2 part B, and scenario 3.)
|
||||
if (message.type === DiscordTypes.MessageType.Reply && message.message_reference?.message_id) {
|
||||
const row = await getHistoricalEventRow(message.message_reference?.message_id)
|
||||
if (row && "event_id" in row) {
|
||||
const row = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index").select("event_id", "room_id", "reference_channel_id", "source").and("WHERE message_id = ? ORDER BY part ASC").get(message.message_reference.message_id)
|
||||
if (row) {
|
||||
repliedToEventRow = Object.assign(row, {channel_id: row.reference_channel_id})
|
||||
} else if (message.referenced_message) {
|
||||
repliedToUnknownEvent = true
|
||||
|
|
@ -300,8 +295,8 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
assert(message.embeds[0].description)
|
||||
const match = message.embeds[0].description.match(/\/channels\/[0-9]*\/[0-9]*\/([0-9]{2,})/)
|
||||
if (match) {
|
||||
const row = await getHistoricalEventRow(match[1])
|
||||
if (row && "event_id" in row) {
|
||||
const row = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index").select("event_id", "room_id", "reference_channel_id", "source").and("WHERE message_id = ? ORDER BY part ASC").get(match[1])
|
||||
if (row) {
|
||||
/*
|
||||
we generate a partial referenced_message based on what PK provided. we don't need everything, since this will only be used for further message-to-event converting.
|
||||
the following properties are necessary:
|
||||
|
|
@ -346,34 +341,6 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} messageID
|
||||
* @param {string} [timestampChannelID]
|
||||
*/
|
||||
async function getHistoricalEventRow(messageID, timestampChannelID) {
|
||||
/** @type {{room_id: string} | {event_id: string, room_id: string, reference_channel_id: string, source: number} | null} */
|
||||
let row = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index")
|
||||
.select("event_id", "room_id", "reference_channel_id", "source").where({message_id: messageID}).and("ORDER BY part ASC").get()
|
||||
if (!row && timestampChannelID) {
|
||||
const ts = dUtils.snowflakeToTimestampExact(messageID)
|
||||
const oldestRow = from("historical_channel_room").selectUnsafe("max(upgraded_timestamp)", "room_id")
|
||||
.where({reference_channel_id: timestampChannelID}).and("and upgraded_timestamp < ?").get(ts)
|
||||
if (oldestRow?.room_id) {
|
||||
row = {room_id: oldestRow.room_id}
|
||||
try {
|
||||
const {event_id} = await di.api.getEventForTimestamp(oldestRow.room_id, ts)
|
||||
row = {
|
||||
event_id,
|
||||
room_id: oldestRow.room_id,
|
||||
reference_channel_id: oldestRow.reference_channel_id,
|
||||
source: 1
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate Discord message links to Matrix event links.
|
||||
* If OOYE has handled this message in the past, this is an instant database lookup.
|
||||
|
|
@ -386,11 +353,30 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
assert(typeof match.index === "number")
|
||||
const [_, channelID, messageID] = match
|
||||
const result = await (async () => {
|
||||
const row = await getHistoricalEventRow(messageID, channelID)
|
||||
if (!row) return `${match[0]} [event is from another server]`
|
||||
const row = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index")
|
||||
.select("event_id", "room_id").where({message_id: messageID}).get()
|
||||
// const roomID = select("channel_room", "room_id", {channel_id: channelID}).pluck().get()
|
||||
if (row) {
|
||||
const via = await getViaServersMemo(row.room_id)
|
||||
if (!("event_id" in row)) return `[unknown event in https://matrix.to/#/${row.room_id}?${via}]`
|
||||
return `https://matrix.to/#/${row.room_id}/${row.event_id}?${via}`
|
||||
}
|
||||
|
||||
const ts = dUtils.snowflakeToTimestampExact(messageID)
|
||||
const oldestRow = from("historical_channel_room").selectUnsafe("max(upgraded_timestamp)", "room_id")
|
||||
.where({reference_channel_id: channelID}).and("and upgraded_timestamp < ?").get(ts)
|
||||
if (oldestRow?.room_id) {
|
||||
const via = await getViaServersMemo(oldestRow.room_id)
|
||||
try {
|
||||
const {event_id} = await di.api.getEventForTimestamp(oldestRow.room_id, ts)
|
||||
return `https://matrix.to/#/${oldestRow.room_id}/${event_id}?${via}`
|
||||
} catch (e) {
|
||||
// M_NOT_FOUND: Unable to find event from <ts> in direction Direction.FORWARDS
|
||||
// not supported in Conduit and descendants
|
||||
return `[unknown event, timestamp resolution failed, in room: https://matrix.to/#/${oldestRow.room_id}?${via}]`
|
||||
}
|
||||
}
|
||||
|
||||
return `${match[0]} [event is from another server]`
|
||||
})()
|
||||
|
||||
content = content.slice(0, match.index + offset) + result + content.slice(match.index + match[0].length + offset)
|
||||
|
|
@ -406,7 +392,6 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
return content.replace(/https:\/\/(cdn|media)\.discordapp\.(?:com|net)\/attachments\/([0-9]+)\/([0-9]+)\/([-A-Za-z0-9_.,]+)/g, url => dUtils.getPublicUrlForCdn(url))
|
||||
}
|
||||
|
||||
const spoilers = []
|
||||
/**
|
||||
* Translate links and emojis and mentions and stuff. Give back the text and HTML so they can be combined into bigger events.
|
||||
* @param {string} content Partial or complete Discord message content
|
||||
|
|
@ -446,7 +431,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
}
|
||||
|
||||
let html = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
|
||||
discordCallback: getDiscordParseCallbacks(message, guild, true, spoilers),
|
||||
discordCallback: getDiscordParseCallbacks(message, guild, true),
|
||||
...customOptions
|
||||
}, customParser, customHtmlOutput)
|
||||
|
||||
|
|
@ -570,16 +555,17 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
// Forwarded content appears first
|
||||
if (message.message_reference?.type === DiscordTypes.MessageReferenceType.Forward && message.message_snapshots?.length) {
|
||||
// Forwarded notice
|
||||
const row = await getHistoricalEventRow(message.message_reference.message_id, message.message_reference.channel_id)
|
||||
const event = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index")
|
||||
.select("event_id", "room_id").where({message_id: message.message_reference.message_id}).get()
|
||||
const room = select("channel_room", ["room_id", "name", "nick"], {channel_id: message.message_reference.channel_id}).get()
|
||||
const forwardedNotice = new mxUtils.MatrixStringBuilder()
|
||||
if (room) {
|
||||
const roomName = room && (room.nick || room.name)
|
||||
if ("event_id" in row) {
|
||||
const via = await getViaServersMemo(row.room_id)
|
||||
if (event) {
|
||||
const via = await getViaServersMemo(event.room_id)
|
||||
forwardedNotice.addLine(
|
||||
`[🔀 Forwarded from #${roomName}]`,
|
||||
tag`🔀 <em>Forwarded from ${roomName} <a href="https://matrix.to/#/${room.room_id}/${row.event_id}?${via}">[jump to event]</a></em>`
|
||||
tag`🔀 <em>Forwarded from ${roomName} <a href="https://matrix.to/#/${room.room_id}/${event.event_id}?${via}">[jump to event]</a></em>`
|
||||
)
|
||||
} else {
|
||||
const via = await getViaServersMemo(room.room_id)
|
||||
|
|
@ -706,13 +692,6 @@ async function messageToEvent(message, guild, options = {}, di) {
|
|||
continue // If discord creates an embed preview for a discord channel link, don't copy that embed
|
||||
}
|
||||
|
||||
if (embed.url && spoilers.some(sp => sp.match(/\bhttps?:\/\/[a-z]/))) {
|
||||
// If the original message had spoilered URLs, don't generate any embeds for links.
|
||||
// This logic is the same as the Discord desktop client. It doesn't match specific embeds to specific spoilered text, it's all or nothing.
|
||||
// It's not easy to do much better because posting a link like youtu.be generates an embed.url with youtube.com/watch, so you can't match up the text without making at least that a special case.
|
||||
continue
|
||||
}
|
||||
|
||||
// Start building up a replica ("rep") of the embed in Discord-markdown format, which we will convert into both plaintext and formatted body at once
|
||||
const rep = new mxUtils.MatrixStringBuilder()
|
||||
|
||||
|
|
|
|||
|
|
@ -287,10 +287,10 @@ test("message2event: message timestamp failed to fetch", async t => {
|
|||
"m.mentions": {},
|
||||
msgtype: "m.text",
|
||||
body: "Me: I'll scroll up to find a certain message I'll send\n_scrolls up and clicks message links for god knows how long_\n_completely forgets what they were looking for and simply begins scrolling up to find some fun moments_\n_stumbles upon:_ "
|
||||
+ "[unknown event in https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org]",
|
||||
+ "[unknown event, timestamp resolution failed, in room: https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org]",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "Me: I'll scroll up to find a certain message I'll send<br><em>scrolls up and clicks message links for god knows how long</em><br><em>completely forgets what they were looking for and simply begins scrolling up to find some fun moments</em><br><em>stumbles upon:</em> "
|
||||
+ '[unknown event in <a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org">https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org</a>]'
|
||||
+ '[unknown event, timestamp resolution failed, in room: <a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org">https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe?via=cadence.moe&via=matrix.org</a>]'
|
||||
}])
|
||||
t.equal(called, 2, "getEventForTimestamp and getJoinedMembers should be called once each")
|
||||
})
|
||||
|
|
@ -862,20 +862,6 @@ test("message2event: advanced written @mentions for matrix users", async t => {
|
|||
t.equal(called, 1, "should only look up the member list once")
|
||||
})
|
||||
|
||||
test("message2event: spoilers are removed from plaintext body", async t => {
|
||||
const events = await messageToEvent({
|
||||
content: "||**beatrice**||"
|
||||
})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
msgtype: "m.text",
|
||||
body: "[spoiler]",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: `<span data-mx-spoiler=""><strong>beatrice</strong></span>`
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event: very large attachment is linked instead of being uploaded", async t => {
|
||||
const events = await messageToEvent({
|
||||
content: "hey",
|
||||
|
|
|
|||
|
|
@ -4,27 +4,20 @@ const assert = require("assert").strict
|
|||
const Ty = require("../../types")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, as, sync, db, select, from} = passthrough
|
||||
const {discord, sync, db, select} = passthrough
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../converters/emoji")} */
|
||||
const emoji = sync.require("../converters/emoji")
|
||||
/** @type {import("../../d2m/actions/retrigger")} */
|
||||
const retrigger = sync.require("../../d2m/actions/retrigger")
|
||||
|
||||
/**
|
||||
* @param {Ty.Event.Outer<Ty.Event.M_Reaction>} event
|
||||
*/
|
||||
async function addReaction(event) {
|
||||
// Wait until the corresponding channel and message have already been bridged
|
||||
if (retrigger.eventNotFoundThenRetrigger(event.content["m.relates_to"].event_id, as.emit.bind(as, "type:m.reaction", event))) return
|
||||
|
||||
// These will exist because it passed retrigger
|
||||
const row = from("event_message").join("message_room", "message_id").join("historical_channel_room", "historical_room_index")
|
||||
.select("message_id", "reference_channel_id").where({event_id: event.content["m.relates_to"].event_id}).and("ORDER BY reaction_part ASC").get()
|
||||
assert(row)
|
||||
const messageID = row.message_id
|
||||
const channelID = row.reference_channel_id
|
||||
const channelID = select("historical_channel_room", "reference_channel_id", {room_id: event.room_id}).pluck().get()
|
||||
if (!channelID) return // We just assume the bridge has already been created
|
||||
const messageID = select("event_message", "message_id", {event_id: event.content["m.relates_to"].event_id}, "ORDER BY reaction_part").pluck().get()
|
||||
if (!messageID) return // Nothing can be done if the parent message was never bridged.
|
||||
|
||||
const key = event.content["m.relates_to"].key
|
||||
const discordPreferredEncoding = await emoji.encodeEmoji(key, event.content.shortcode)
|
||||
|
|
@ -42,10 +35,6 @@ async function addReaction(event) {
|
|||
// happens if a matrix user tries to add on to a super reaction
|
||||
return
|
||||
}
|
||||
if (e.message?.includes("Unknown Message")) {
|
||||
// happens under a race condition where a message is deleted after it passes the database check above
|
||||
return
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ const DiscordTypes = require("discord-api-types/v10")
|
|||
const Ty = require("../../types")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, as, sync, db, select, from} = passthrough
|
||||
const {discord, sync, db, select, from} = passthrough
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../../d2m/actions/retrigger")} */
|
||||
const retrigger = sync.require("../../d2m/actions/retrigger")
|
||||
|
||||
/**
|
||||
* @param {Ty.Event.Outer_M_Room_Redaction} event
|
||||
|
|
@ -54,18 +52,13 @@ async function removeReaction(event) {
|
|||
* @param {Ty.Event.Outer_M_Room_Redaction} event
|
||||
*/
|
||||
async function handle(event) {
|
||||
// If this is for removing a reaction, try it
|
||||
await removeReaction(event)
|
||||
|
||||
// Or, it might be for removing a message or suppressing embeds. But to do that, the message needs to be bridged first.
|
||||
if (retrigger.eventNotFoundThenRetrigger(event.redacts, as.emit.bind(as, "type:m.room.redaction", event))) return
|
||||
|
||||
const row = select("event_message", ["event_type", "event_subtype", "part"], {event_id: event.redacts}).get()
|
||||
if (row && row.event_type === "m.room.message" && row.event_subtype === "m.notice" && row.part === 1) {
|
||||
await suppressEmbeds(event)
|
||||
} else {
|
||||
await deleteMessage(event)
|
||||
}
|
||||
await removeReaction(event)
|
||||
}
|
||||
|
||||
module.exports.handle = handle
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ const api = sync.require("../matrix/api")
|
|||
const createRoom = sync.require("../d2m/actions/create-room")
|
||||
/** @type {import("../matrix/room-upgrade")} */
|
||||
const roomUpgrade = require("../matrix/room-upgrade")
|
||||
/** @type {import("../d2m/actions/retrigger")} */
|
||||
const retrigger = sync.require("../d2m/actions/retrigger")
|
||||
const {reg} = require("../matrix/read-registration")
|
||||
|
||||
let lastReportedEvent = 0
|
||||
|
|
@ -203,7 +201,6 @@ async event => {
|
|||
// @ts-ignore
|
||||
await matrixCommandHandler.execute(event)
|
||||
}
|
||||
retrigger.messageFinishedBridging(event.event_id)
|
||||
await api.ackEvent(event)
|
||||
}))
|
||||
|
||||
|
|
@ -214,7 +211,6 @@ sync.addTemporaryListener(as, "type:m.sticker", guard("m.sticker",
|
|||
async event => {
|
||||
if (utils.eventSenderIsFromDiscord(event.sender)) return
|
||||
const messageResponses = await sendEvent.sendEvent(event)
|
||||
retrigger.messageFinishedBridging(event.event_id)
|
||||
await api.ackEvent(event)
|
||||
}))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue