forked from cadence/out-of-your-element
Backfill missed pins and pins from the past
This commit is contained in:
parent
ed7404ea19
commit
8987107685
9 changed files with 147 additions and 13 deletions
|
@ -1,22 +1,37 @@
|
|||
// @ts-check
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync} = passthrough
|
||||
const {discord, sync, db} = passthrough
|
||||
/** @type {import("../converters/pins-to-list")} */
|
||||
const pinsToList = sync.require("../converters/pins-to-list")
|
||||
/** @type {import("../../matrix/api")} */
|
||||
const api = sync.require("../../matrix/api")
|
||||
|
||||
/**
|
||||
* @param {string} channelID
|
||||
* @param {string} roomID
|
||||
* @template {string | null | undefined} T
|
||||
* @param {T} timestamp
|
||||
* @returns {T extends string ? number : null}
|
||||
*/
|
||||
async function updatePins(channelID, roomID) {
|
||||
const pins = await discord.snow.channel.getChannelPinnedMessages(channelID)
|
||||
const eventIDs = pinsToList.pinsToList(pins)
|
||||
await api.sendState(roomID, "m.room.pinned_events", "", {
|
||||
pinned: eventIDs
|
||||
})
|
||||
function convertTimestamp(timestamp) {
|
||||
// @ts-ignore
|
||||
return typeof timestamp === "string" ? Math.floor(new Date(timestamp).getTime() / 1000) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} channelID
|
||||
* @param {string} roomID
|
||||
* @param {number?} convertedTimestamp
|
||||
*/
|
||||
async function updatePins(channelID, roomID, convertedTimestamp) {
|
||||
const pins = await discord.snow.channel.getChannelPinnedMessages(channelID)
|
||||
const eventIDs = pinsToList.pinsToList(pins)
|
||||
if (pins.length === eventIDs.length || eventIDs.length) {
|
||||
await api.sendState(roomID, "m.room.pinned_events", "", {
|
||||
pinned: eventIDs
|
||||
})
|
||||
}
|
||||
db.prepare("UPDATE channel_room SET last_bridged_pin_timestamp = ? WHERE channel_id = ?").run(convertedTimestamp || 0, channelID)
|
||||
}
|
||||
|
||||
module.exports.convertTimestamp = convertTimestamp
|
||||
module.exports.updatePins = updatePins
|
||||
|
|
|
@ -6,7 +6,7 @@ test("pins2list: converts known IDs, ignores unknown IDs", t => {
|
|||
const result = pinsToList(data.pins.faked)
|
||||
t.deepEqual(result, [
|
||||
"$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4",
|
||||
"$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuAno",
|
||||
"$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA",
|
||||
"$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg"
|
||||
])
|
||||
})
|
||||
|
|
|
@ -43,6 +43,7 @@ const utils = {
|
|||
}
|
||||
if (listen === "full") {
|
||||
eventDispatcher.checkMissedExpressions(message.d)
|
||||
eventDispatcher.checkMissedPins(client, message.d)
|
||||
eventDispatcher.checkMissedMessages(client, message.d)
|
||||
}
|
||||
|
||||
|
@ -94,6 +95,13 @@ const utils = {
|
|||
client.channels.set(message.d.id, message.d)
|
||||
|
||||
|
||||
} else if (message.t === "CHANNEL_PINS_UPDATE") {
|
||||
const channel = client.channels.get(message.d.channel_id)
|
||||
if (channel) {
|
||||
channel["last_pin_timestamp"] = message.d.last_pin_timestamp
|
||||
}
|
||||
|
||||
|
||||
} else if (message.t === "GUILD_DELETE") {
|
||||
client.guilds.delete(message.d.id)
|
||||
const channels = client.guildChannelMap.get(message.d.id)
|
||||
|
|
|
@ -25,9 +25,15 @@ const createSpace = sync.require("./actions/create-space")
|
|||
const updatePins = sync.require("./actions/update-pins")
|
||||
/** @type {import("../matrix/api")}) */
|
||||
const api = sync.require("../matrix/api")
|
||||
/** @type {import("../discord/utils")} */
|
||||
const utils = sync.require("../discord/utils")
|
||||
/** @type {import("../discord/discord-command-handler")}) */
|
||||
const discordCommandHandler = sync.require("../discord/discord-command-handler")
|
||||
|
||||
/** @type {any} */ // @ts-ignore bad types from semaphore
|
||||
const Semaphore = require("@chriscdn/promise-semaphore")
|
||||
const checkMissedPinsSema = new Semaphore()
|
||||
|
||||
let lastReportedEvent = 0
|
||||
|
||||
// Grab Discord events we care about for the bridge, check them, and pass them on
|
||||
|
@ -103,6 +109,14 @@ module.exports = {
|
|||
const latestWasBridged = prepared.get(channel.last_message_id)
|
||||
if (latestWasBridged) continue
|
||||
|
||||
// Permissions check
|
||||
const member = guild.members.find(m => m.user?.id === client.user.id)
|
||||
if (!member) return
|
||||
if (!("permission_overwrites" in channel)) continue
|
||||
const permissions = utils.getPermissions(member.roles, guild.roles, client.user.id, channel.permission_overwrites)
|
||||
const wants = BigInt(1 << 10) | BigInt(1 << 16) // VIEW_CHANNEL + READ_MESSAGE_HISTORY
|
||||
if ((permissions & wants) !== wants) continue // We don't have permission to look back in this channel
|
||||
|
||||
/** More recent messages come first. */
|
||||
// console.log(`[check missed messages] in ${channel.id} (${guild.name} / ${channel.name}) because its last message ${channel.last_message_id} is not in the database`)
|
||||
let messages
|
||||
|
@ -132,6 +146,34 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When logging back in, check if the pins on Matrix-side are up to date. If they aren't, update all pins.
|
||||
* Rather than query every room on Matrix-side, we cache the latest pinned message in the database and compare against that.
|
||||
* @param {import("./discord-client")} client
|
||||
* @param {DiscordTypes.GatewayGuildCreateDispatchData} guild
|
||||
*/
|
||||
async checkMissedPins(client, guild) {
|
||||
if (guild.unavailable) return
|
||||
const member = guild.members.find(m => m.user?.id === client.user.id)
|
||||
if (!member) return
|
||||
for (const channel of guild.channels) {
|
||||
if (!("last_pin_timestamp" in channel) || !channel.last_pin_timestamp) continue // Only care about channels that have pins
|
||||
if (!("permission_overwrites" in channel)) continue
|
||||
const lastPin = updatePins.convertTimestamp(channel.last_pin_timestamp)
|
||||
|
||||
// Permissions check
|
||||
const permissions = utils.getPermissions(member.roles, guild.roles, client.user.id, channel.permission_overwrites)
|
||||
const wants = BigInt(1 << 10) | BigInt(1 << 16) // VIEW_CHANNEL + READ_MESSAGE_HISTORY
|
||||
if ((permissions & wants) !== wants) continue // We don't have permission to look up the pins in this channel
|
||||
|
||||
const row = select("channel_room", ["room_id", "last_bridged_pin_timestamp"], {channel_id: channel.id}).get()
|
||||
if (!row) continue // Only care about already bridged channels
|
||||
if (row.last_bridged_pin_timestamp == null || lastPin > row.last_bridged_pin_timestamp) {
|
||||
checkMissedPinsSema.request(() => updatePins.updatePins(channel.id, row.room_id, lastPin))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When logging back in, check if we missed any changes to emojis or stickers. Apply the changes if so.
|
||||
* @param {DiscordTypes.GatewayGuildCreateDispatchData} guild
|
||||
|
@ -183,7 +225,8 @@ module.exports = {
|
|||
async onChannelPinsUpdate(client, data) {
|
||||
const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get()
|
||||
if (!roomID) return // No target room to update pins in
|
||||
await updatePins.updatePins(data.channel_id, roomID)
|
||||
const convertedTimestamp = updatePins.convertTimestamp(data.last_pin_timestamp)
|
||||
await updatePins.updatePins(data.channel_id, roomID, convertedTimestamp)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue