Sync pins back from Matrix to Discord

This commit is contained in:
Cadence Ember
2025-01-07 12:23:39 +13:00
parent 4c62124cee
commit 06b6a63ee3
6 changed files with 87 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
// @ts-check
const {sync, from, discord} = require("../../passthrough")
/** @type {import("../converters/diff-pins")} */
const diffPins = sync.require("../converters/diff-pins")
/**
* @param {string[]} pins
* @param {string[]} prev
*/
async function updatePins(pins, prev) {
const diff = diffPins.diffPins(pins, prev)
for (const [event_id, added] of diff) {
const row = from("event_message").join("message_channel", "message_id").where({event_id}).select("channel_id", "message_id").get()
if (!row) continue
if (added) {
discord.snow.channel.addChannelPinnedMessage(row.channel_id, row.message_id, "Message pinned on Matrix")
} else {
discord.snow.channel.removeChannelPinnedMessage(row.channel_id, row.message_id, "Message unpinned on Matrix")
}
}
}
module.exports.updatePins = updatePins

View File

@@ -0,0 +1,17 @@
// @ts-check
/**
* @param {string[]} pins
* @param {string[]} prev
* @returns {[string, boolean][]}
*/
function diffPins(pins, prev) {
/** @type {[string, boolean][]} */
const result = []
return result.concat(
prev.filter(id => !pins.includes(id)).map(id => [id, false]), // removed
pins.filter(id => !prev.includes(id)).map(id => [id, true]) // added
)
}
module.exports.diffPins = diffPins

View File

@@ -0,0 +1,11 @@
// @ts-check
const {test} = require("supertape")
const diffPins = require("./diff-pins")
test("diff pins: diff is as expected", t => {
t.deepEqual(
diffPins.diffPins(["same", "new"], ["same", "old"]),
[["old", false], ["new", true]]
)
})

View File

@@ -14,6 +14,8 @@ const sendEvent = sync.require("./actions/send-event")
const addReaction = sync.require("./actions/add-reaction")
/** @type {import("./actions/redact")} */
const redact = sync.require("./actions/redact")
/** @type {import("./actions/update-pins")}) */
const updatePins = sync.require("./actions/update-pins")
/** @type {import("../matrix/matrix-command-handler")} */
const matrixCommandHandler = sync.require("../matrix/matrix-command-handler")
/** @type {import("./converters/utils")} */
@@ -159,6 +161,33 @@ async event => {
db.prepare("UPDATE channel_room SET nick = ? WHERE room_id = ?").run(name, event.room_id)
}))
sync.addTemporaryListener(as, "type:m.room.pinned_events", guard("m.room.pinned_events",
/**
* @param {Ty.Event.StateOuter<Ty.Event.M_Room_PinnedEvents>} event
*/
async event => {
if (event.state_key !== "") return
if (utils.eventSenderIsFromDiscord(event.sender)) return
const pins = event.content.pinned
if (!Array.isArray(pins)) return
let prev = event.unsigned?.prev_content?.pinned
if (!Array.isArray(prev)) {
if (pins.length === 1) {
/*
In edge cases, prev_content isn't guaranteed to be provided by the server.
If prev_content is missing, we can't diff. Better safe than sorry: we'd like to ignore the change rather than wiping the whole channel's pins on Discord.
However, that would mean if the first ever pin came from Matrix-side, it would be ignored, because there would be no prev_content (it's the first pinned event!)
So to handle that edge case, we assume that if there's exactly 1 entry in `pinned`, this is the first ever pin and it should go through.
*/
prev = []
} else {
return
}
}
await updatePins.updatePins(pins, prev)
}))
sync.addTemporaryListener(as, "type:m.room.member", guard("m.room.member",
/**
* @param {Ty.Event.StateOuter<Ty.Event.M_Room_Member>} event

4
src/types.d.ts vendored
View File

@@ -241,6 +241,10 @@ export namespace Event {
name?: string
}
export type M_Room_PinnedEvents = {
pinned: string[]
}
export type M_Power_Levels = {
/** The level required to ban a user. Defaults to 50 if unspecified. */
ban?: number,

View File

@@ -139,6 +139,7 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not
require("../src/d2m/converters/remove-reaction.test")
require("../src/d2m/converters/thread-to-announcement.test")
require("../src/d2m/converters/user-to-mxid.test")
require("../src/m2d/converters/diff-pins.test")
require("../src/m2d/converters/event-to-message.test")
require("../src/m2d/converters/utils.test")
require("../src/m2d/converters/emoji-sheet.test")