diff --git a/docs/get-started.md b/docs/get-started.md index 17c98de..5b14b2a 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -56,16 +56,8 @@ After that, to get into the rooms on your Matrix account, use the invite form on I hope you enjoy Out Of Your Element! -# Extras - -## /plu/ral support - -To support /plu/ral webhook proxying, OOYE needs to call the /plu/ral API authenticated. I can't provide an API key for everyone to share, so you have to make your own. Here's how: - -1. Open the [/plu/ral app on Discord](https://discord.com/discovery/applications/1291501048493768784) and "Add App" -> "Add to My Apps (use everywhere)" -2. Go to any text channel on Discord and use the new `/api` command from /plu/ral. -3. Create a new application named Out Of Your Element. Copy the displayed token. -3. Run `node scripts/plu-ral-api.js` and paste the token. +---- +




# Appendix diff --git a/scripts/plu-ral-api.js b/scripts/plu-ral-api.js deleted file mode 100644 index d3468a3..0000000 --- a/scripts/plu-ral-api.js +++ /dev/null @@ -1,17 +0,0 @@ -// @ts-check - -const {reg, writeRegistration} = require("../src/matrix/read-registration") -const {prompt} = require("enquirer") - -;(async () => { - /** @type {{api_key: string}} */ - const apiKeyResponse = await prompt({ - type: "text", - name: "api_key", - message: "Paste your personal /plu/ral API key" - }) - - reg.ooye.plu_ral_api_key = apiKeyResponse.api_key - writeRegistration(reg) - console.log("Saved. This change should be applied instantly.") -})() diff --git a/src/d2m/actions/edit-message.js b/src/d2m/actions/edit-message.js index 31f3627..f86a9c8 100644 --- a/src/d2m/actions/edit-message.js +++ b/src/d2m/actions/edit-message.js @@ -1,24 +1,24 @@ // @ts-check +const assert = require("assert").strict + const passthrough = require("../../passthrough") const {sync, db, select, from} = passthrough -const {reg} = require("../../matrix/read-registration") /** @type {import("../converters/edit-to-changes")} */ const editToChanges = sync.require("../converters/edit-to-changes") -/** @type {import("./speedbump")} */ -const speedbump = sync.require("./speedbump") +/** @type {import("./register-pk-user")} */ +const registerPkUser = sync.require("./register-pk-user") /** @type {import("../../matrix/api")} */ const api = sync.require("../../matrix/api") /** @type {import("../../matrix/mreq")} */ const mreq = sync.require("../../matrix/mreq") -/** @type {import("../../discord/utils")} */ -const dUtils = sync.require("../../discord/utils") /** * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message * @param {import("discord-api-types/v10").APIGuild} guild + * @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel */ -async function editMessage(message, guild) { +async function editMessage(message, guild, row) { const historicalRoomOfMessage = from("message_room").join("historical_channel_room", "historical_room_index").where({message_id: message.id}).select("room_id").get() const currentRoom = from("channel_room").join("historical_channel_room", "room_id").where({channel_id: message.channel_id}).select("room_id", "historical_room_index").get() if (!currentRoom) return @@ -27,9 +27,11 @@ async function editMessage(message, guild) { let {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid, promotions} = await editToChanges.editToChanges(message, guild, api) - // Sync proxy user profile (if sent by proxy) - if (dUtils.isWebhookMessage(message)) { - senderMxid = await speedbump.getWebhookSenderId(message, guild.id, roomID) + if (row && row.speedbump_webhook_id === message.webhook_id) { + // Handle the PluralKit public instance + if (row.speedbump_id === "466378653216014359") { + senderMxid = await registerPkUser.syncUser(message.id, message.author, roomID, true) + } } // 1. Replace all the things. diff --git a/src/d2m/actions/register-pk-user.js b/src/d2m/actions/register-pk-user.js index 4946d4f..6ecd077 100644 --- a/src/d2m/actions/register-pk-user.js +++ b/src/d2m/actions/register-pk-user.js @@ -1,6 +1,6 @@ // @ts-check -const assert = require("assert").strict +const assert = require("assert") const {reg} = require("../../matrix/read-registration") const Ty = require("../../types") @@ -132,10 +132,10 @@ async function syncUser(messageID, author, roomID, shouldActuallySync) { try { // API lookup var pkMessage = await fetchMessage(messageID) - db.prepare("REPLACE INTO sim_proxy (user_id, proxy_owner_id, displayname, proxy_app) VALUES (?, ?, ?, 0)").run(pkMessage.member.uuid, pkMessage.sender, author.username) + db.prepare("REPLACE INTO sim_proxy (user_id, proxy_owner_id, displayname) VALUES (?, ?, ?)").run(pkMessage.member.uuid, pkMessage.sender, author.username) } catch (e) { // Fall back to offline cache - const senderMxid = from("sim_proxy").join("sim", "user_id").join("sim_member", "mxid").where({displayname: author.username, room_id: roomID, proxy_app: 0}).pluck("mxid").get() + const senderMxid = from("sim_proxy").join("sim", "user_id").join("sim_member", "mxid").where({displayname: author.username, room_id: roomID}).pluck("mxid").get() if (!senderMxid) throw e return senderMxid } diff --git a/src/d2m/actions/register-plu-ral-user.js b/src/d2m/actions/register-plu-ral-user.js deleted file mode 100644 index 04fa28c..0000000 --- a/src/d2m/actions/register-plu-ral-user.js +++ /dev/null @@ -1,170 +0,0 @@ -// @ts-check - -const assert = require("assert").strict -const {reg} = require("../../matrix/read-registration") -const Ty = require("../../types") - -const passthrough = require("../../passthrough") -const {sync, db, select, from} = passthrough -/** @type {import("../../matrix/api")} */ -const api = sync.require("../../matrix/api") -/** @type {import("../../matrix/file")} */ -const file = sync.require("../../matrix/file") -/** @type {import("./register-user")} */ -const registerUser = sync.require("./register-user") - -/** @returns {Promise} */ -async function fetchMessage(channelID, messageID) { - assert(reg.ooye.plu_ral_api_key) - try { - var res = await fetch(`https://api.plural.gg/messages/${channelID}/${messageID}?member=true`, { - headers: { - Authorization: reg.ooye.plu_ral_api_key - } - }) - } catch (networkError) { - // Network issue, raise a more readable message - throw new Error(`Failed to connect to /plu/ral API: ${networkError.toString()}`) - } - if (!res.ok) throw new Error(`/plu/ral API returned an error: ${await res.text()}`) - /** @type {any} */ - const root = await res.json() - if (!root.member) throw new Error(`/plu/ral API didn't return member data: ${JSON.stringify(root)}`) - return root -} - -/** - * Using the same sim names and fake user IDs for /plu/ral members, since unlike PluralKit they don't have a short and a long ID. - * @param {Ty.PluRalWebhookMessage} pluRalMessage - */ -function getSimName(pluRalMessage) { - return `_pl_${pluRalMessage.member_id}` -} - -/** - * A sim is an account that is being simulated by the bridge to copy events from the other side. - * @param {Ty.PluRalWebhookMessage} pluRalMessage - * @returns mxid - */ -async function createSim(pluRalMessage) { - // Choose sim name - const simName = getSimName(pluRalMessage) - const localpart = reg.ooye.namespace_prefix + simName - const mxid = `@${localpart}:${reg.ooye.server_name}` - - // Save chosen name in the database forever - db.prepare("INSERT INTO sim (user_id, username, sim_name, mxid) VALUES (?, ?, ?, ?)").run(simName, simName, simName, mxid) - - // Register matrix user with that name - try { - await api.register(localpart) - } catch (e) { - // If user creation fails, manually undo the database change. Still isn't perfect, but should help. - // (I would prefer a transaction, but it's not safe to leave transactions open across event loop ticks.) - db.prepare("DELETE FROM sim WHERE user_id = ?").run(simName) - throw e - } - return mxid -} - -/** - * Ensure a sim is registered for the user. - * If there is already a sim, use that one. If there isn't one yet, register a new sim. - * @param {Ty.PluRalWebhookMessage} pluRalMessage - * @returns {Promise} mxid - */ -async function ensureSim(pluRalMessage) { - let mxid = null - const existing = select("sim", "mxid", {user_id: getSimName(pluRalMessage)}).pluck().get() - if (existing) { - mxid = existing - } else { - mxid = await createSim(pluRalMessage) - } - return mxid -} - -/** - * Ensure a sim is registered for the user and is joined to the room. - * @param {Ty.PluRalWebhookMessage} pluRalMessage - * @param {string} roomID - * @returns {Promise} mxid - */ -async function ensureSimJoined(pluRalMessage, roomID) { - // Ensure room ID is really an ID, not an alias - assert.ok(roomID[0] === "!") - - // Ensure user - const mxid = await ensureSim(pluRalMessage) - - // Ensure joined - const existing = select("sim_member", "mxid", {room_id: roomID, mxid}).pluck().get() - if (!existing) { - await api.inviteToRoom(roomID, mxid) - await api.joinRoom(roomID, mxid) - db.prepare("INSERT OR IGNORE INTO sim_member (room_id, mxid) VALUES (?, ?)").run(roomID, mxid) - } - return mxid -} - -/** - * Generate profile data based on webhook displayname and configured avatar. - * @param {Ty.PluRalWebhookMessage} pluRalMessage - * @param {Ty.WebhookAuthor} author - */ -async function memberToStateContent(pluRalMessage, author) { - // We prefer to use the member's avatar URL data since the image upload can be cached across channels, - // unlike the userAvatar URL which is unique per channel, due to the webhook ID being in the URL. - const avatar = pluRalMessage.member.avatar_url || file.userAvatar(author) - - const content = { - displayname: author.username, - membership: "join", - "moe.cadence.ooye.plu_ral_member": pluRalMessage.member - } - if (avatar) content.avatar_url = await file.uploadDiscordFileToMxc(avatar) - - return content -} - -/** - * Sync profile data for a sim user. This function follows the following process: - * 1. Look up data about proxy user from API - * 2. If this fails, try to use previously cached data (won't sync) - * 3. Create and join the sim to the room if needed - * 4. Make an object of what the new room member state content would be, including uploading the profile picture if it hasn't been done before - * 5. Compare against the previously known state content, which is helpfully stored in the database - * 6. If the state content has changed, send it to Matrix and update it in the database for next time - * @param {string} channelID to call API with - * @param {string} messageID to call API with - * @param {Ty.WebhookAuthor} author for profile data - * @param {string} roomID room to join member to - * @param {boolean} shouldActuallySync whether to actually sync updated user data or just ensure it's joined - * @returns {Promise} mxid of the updated sim - */ -async function syncUser(channelID, messageID, author, roomID, shouldActuallySync) { - try { - // API lookup - var pluRalMessage = await fetchMessage(channelID, messageID) - const simName = getSimName(pluRalMessage) - db.prepare("REPLACE INTO sim_proxy (user_id, proxy_owner_id, displayname, proxy_app) VALUES (?, ?, ?, 1)").run(simName, pluRalMessage.author_id, author.username) - } catch (e) { - // Fall back to offline cache - const senderMxid = from("sim_proxy").join("sim", "user_id").join("sim_member", "mxid").where({displayname: author.username, room_id: roomID, proxy_app: 1}).pluck("mxid").get() - if (!senderMxid) throw e - return senderMxid - } - - // Create and join the sim to the room if needed - const mxid = await ensureSimJoined(pluRalMessage, roomID) - - if (shouldActuallySync) { - // Build current profile data and sync if the hash has changed - const content = await memberToStateContent(pluRalMessage, author) - await registerUser._sendSyncUser(roomID, mxid, content, null) - } - - return mxid -} - -module.exports.syncUser = syncUser diff --git a/src/d2m/actions/send-message.js b/src/d2m/actions/send-message.js index 41ef70d..5b3b4f3 100644 --- a/src/d2m/actions/send-message.js +++ b/src/d2m/actions/send-message.js @@ -4,16 +4,17 @@ const assert = require("assert").strict const DiscordTypes = require("discord-api-types/v10") const passthrough = require("../../passthrough") -const {discord, sync, db, select, from} = passthrough -const {reg} = require("../../matrix/read-registration") +const { discord, sync, db, select, from} = passthrough /** @type {import("../converters/message-to-event")} */ const messageToEvent = sync.require("../converters/message-to-event") /** @type {import("../../matrix/api")} */ const api = sync.require("../../matrix/api") /** @type {import("./register-user")} */ const registerUser = sync.require("./register-user") -/** @type {import("./speedbump")} */ -const speedbump = sync.require("./speedbump") +/** @type {import("./register-pk-user")} */ +const registerPkUser = sync.require("./register-pk-user") +/** @type {import("./register-webhook-user")} */ +const registerWebhookUser = sync.require("./register-webhook-user") /** @type {import("../actions/create-room")} */ const createRoom = sync.require("../actions/create-room") /** @type {import("../actions/poll-end")} */ @@ -27,15 +28,24 @@ const channelWebhook = sync.require("../../m2d/actions/channel-webhook") * @param {DiscordTypes.GatewayMessageCreateDispatchData} message * @param {DiscordTypes.APIGuildChannel} channel * @param {DiscordTypes.APIGuild} guild + * @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel */ -async function sendMessage(message, channel, guild) { +async function sendMessage(message, channel, guild, row) { const roomID = await createRoom.ensureRoom(message.channel_id) const historicalRoomIndex = select("historical_channel_room", "historical_room_index", {room_id: roomID}).pluck().get() assert(historicalRoomIndex) let senderMxid = null if (dUtils.isWebhookMessage(message)) { - senderMxid = await speedbump.getWebhookSenderId(message, guild.id, roomID) + const useWebhookProfile = select("guild_space", "webhook_profile", {guild_id: guild.id}).pluck().get() ?? 0 + if (row && row.speedbump_webhook_id === message.webhook_id) { + // Handle the PluralKit public instance + if (row.speedbump_id === "466378653216014359") { + senderMxid = await registerPkUser.syncUser(message.id, message.author, roomID, true) + } + } else if (useWebhookProfile) { + senderMxid = await registerWebhookUser.syncUser(message.author, roomID, true) + } } else { // not a webhook if (message.author.id === discord.application.id) { diff --git a/src/d2m/actions/speedbump.js b/src/d2m/actions/speedbump.js index 88cea07..42e3a35 100644 --- a/src/d2m/actions/speedbump.js +++ b/src/d2m/actions/speedbump.js @@ -1,23 +1,13 @@ // @ts-check -const DiscordTypes = require("discord-api-types/v10") - const passthrough = require("../../passthrough") -const {discord, from, select, db, sync} = passthrough - -const {reg} = require("../../matrix/read-registration") -/** @type {import("./register-pk-user")} */ -const registerPkUser = sync.require("./register-pk-user") -/** @type {import("./register-plu-ral-user")} */ -const registerPluRalUser = sync.require("./register-plu-ral-user") -/** @type {import("./register-webhook-user")} */ -const registerWebhookUser = sync.require("./register-webhook-user") +const {discord, select, db} = passthrough const DEBUG_SPEEDBUMP = false -function debugSpeedbump(...args) { +function debugSpeedbump(message) { if (DEBUG_SPEEDBUMP) { - console.log(...args) + console.log(message) } } @@ -26,8 +16,7 @@ const SPEEDBUMP_UPDATE_FREQUENCY = 2 * 60 * 60 // 2 hours /** @type {Set} */ const KNOWN_BOTS = new Set([ - "466378653216014359", // PluralKit - "1291501048493768784", // /plu/ral + "466378653216014359" // PluralKit ]) /** @@ -39,81 +28,61 @@ async function updateCache(channelID, lastChecked) { const now = Math.floor(Date.now() / 1000) if (lastChecked && now - lastChecked < SPEEDBUMP_UPDATE_FREQUENCY) return const webhooks = await discord.snow.webhook.getChannelWebhooks(channelID) - const found = webhooks.filter(b => KNOWN_BOTS.has(b.application_id)) - db.transaction(() => { - db.prepare("DELETE FROM channel_speedbump WHERE channel_id = ?").run(channelID) - for (const webhook of found) { - db.prepare("INSERT INTO channel_speedbump (channel_id, speedbump_webhook_id, speedbump_user_id) VALUES (?, ?, ?)").run(channelID, webhook.id, webhook.application_id) - } - db.prepare("UPDATE channel_room SET speedbump_checked = ? WHERE channel_id = ?").run(now, channelID) - })() + const found = webhooks.find(b => KNOWN_BOTS.has(b.application_id)) + const foundApplication = found?.application_id + const foundWebhook = found?.id + db.prepare("UPDATE channel_room SET speedbump_id = ?, speedbump_webhook_id = ?, speedbump_checked = ? WHERE channel_id = ?").run(foundApplication, foundWebhook, now, channelID) } -/** - * @typedef BumpingEntry - * @prop {number} number number of gateway events currently bumping for this message ID - * @prop {boolean} hasCreate whether there was a message create within the events currently bumping - */ - -/** @type {Map} messageID -> BumpingEntry */ +/** @type {Map} messageID -> number of gateway events currently bumping */ const bumping = new Map() /** * Slow down a message. After it passes the speedbump, return whether it's okay or if it's been deleted. - * @param {boolean} isCreate * @param {string} messageID * @returns whether it was deleted */ -async function doSpeedbump(isCreate, messageID) { - const entry = bumping.get(messageID) ?? (() => { - const entry = {number: 0, hasCreate: false} - bumping.set(messageID, entry) - return entry - })() - - entry.number++ - entry.hasCreate ||= isCreate - - debugSpeedbump(`[speedbump] WAIT ${messageID}++ =`, entry) +async function doSpeedbump(messageID) { + let value = (bumping.get(messageID) ?? 0) + 1 + bumping.set(messageID, value) + debugSpeedbump(`[speedbump] WAIT ${messageID}++ = ${value}`) await new Promise(resolve => setTimeout(resolve, SPEEDBUMP_SPEED)) if (!bumping.has(messageID)) { debugSpeedbump(`[speedbump] DELETED ${messageID}`) - return {skip: true, hasCreate: null} + return true } - - if (--entry.number <= 0) { - debugSpeedbump(`[speedbump] OK ${messageID}-- =`, entry) + value = (bumping.get(messageID) ?? 0) - 1 + if (value <= 0) { + debugSpeedbump(`[speedbump] OK ${messageID}-- = ${value}`) bumping.delete(messageID) - return {skip: false, hasCreate: entry.hasCreate} + return false } else { - debugSpeedbump(`[speedbump] MULTI ${messageID}-- =`, entry) - return {skip: true, hasCreate: null} + debugSpeedbump(`[speedbump] MULTI ${messageID}-- = ${value}`) + bumping.set(messageID, value) + return true } } -function getSpeedbumpRows(channelID) { - return from("channel_room").join("channel_speedbump", "channel_id").select("thread_parent", "speedbump_user_id", "speedbump_webhook_id").where({channel_id: channelID}).all() -} - /** * Check whether to slow down a message, and do it. After it passes the speedbump, return whether it's okay or if it's been deleted. - * @param {boolean} isCreate - * @param {{id: string, channel_id: string, author: {id: string}, backfill?: boolean}} message uses the ID to identify, and the userID to only slow down the message when the user has used PK before - * @returns whether to skip this message, and whether the message should be created as a creation + * @param {string} channelID + * @param {string} messageID + * @param {string} [userID] if provided, only slow down the message when the user has used PK before + * @returns whether it was deleted, and data about the channel's (not thread's) speedbump */ -async function maybeDoSpeedbump(isCreate, message) { - let rows = getSpeedbumpRows(message.channel_id) - if (rows[0]?.thread_parent) rows = getSpeedbumpRows(rows[0].thread_parent) // webhooks belong to the channel, not the thread - if (!rows.length) return {skip: false} // channel not affected, no speedbump - - if (message.backfill) return {skip: false} // don't slow messages during backfill - if (rows.some(r => r.speedbump_webhook_id === message.author.id)) return {skip: false} // shortcut - const userHasProxy = select("sim_proxy", "user_id", {proxy_owner_id: message.author.id}).pluck().get() - if (!userHasProxy) return {skip: false} // user has not used PK before, no speedbump - const {skip, hasCreate} = await doSpeedbump(isCreate, message.id) - return {skip, hasCreate} // maybe affected, and there is a speedbump +async function maybeDoSpeedbump(channelID, messageID, userID) { + let row = select("channel_room", ["room_id", "thread_parent", "speedbump_id", "speedbump_webhook_id"], {channel_id: channelID}).get() + if (row?.thread_parent) row = select("channel_room", ["room_id", "thread_parent", "speedbump_id", "speedbump_webhook_id"], {channel_id: row.thread_parent}).get() // webhooks belong to the channel, not the thread + if (!row?.speedbump_webhook_id) return {affected: false, row: null} // channel not affected, no speedbump + if (userID) { + if (row.speedbump_webhook_id === userID) return {affected: false, row} // shortcut + const userHasProxy = select("sim_proxy", "user_id", {proxy_owner_id: userID}).pluck().get() + if (!userHasProxy) return {affected: false, row} // user has not used PK before, no speedbump + } + const affected = await doSpeedbump(messageID) + return {affected, row} // maybe affected, and there is a speedbump } /** @@ -123,25 +92,7 @@ function onMessageDelete(messageID) { bumping.delete(messageID) } -/** - * @param {DiscordTypes.APIMessage} message - * @param {string} guildID - * @param {string} roomID - */ -async function getWebhookSenderId(message, guildID, roomID) { - const speedbumpUserID = select("channel_speedbump", "speedbump_user_id", {channel_id: message.channel_id, speedbump_webhook_id: message.webhook_id}).pluck().get() - const useWebhookProfile = select("guild_space", "webhook_profile", {guild_id: guildID}).pluck().get() ?? 0 - if (speedbumpUserID === "466378653216014359") { // PluralKit public instance - return await registerPkUser.syncUser(message.id, message.author, roomID, true) - } else if (speedbumpUserID === "1291501048493768784" && reg.ooye.plu_ral_api_key) { // /plu/ral public instance - return await registerPluRalUser.syncUser(message.channel_id, message.id, message.author, roomID, true) - } else if (useWebhookProfile) { - return await registerWebhookUser.syncUser(message.author, roomID, true) - } - return null -} - module.exports.updateCache = updateCache +module.exports.doSpeedbump = doSpeedbump module.exports.maybeDoSpeedbump = maybeDoSpeedbump module.exports.onMessageDelete = onMessageDelete -module.exports.getWebhookSenderId = getWebhookSenderId diff --git a/src/d2m/event-dispatcher.js b/src/d2m/event-dispatcher.js index a49453a..c0c33d9 100644 --- a/src/d2m/event-dispatcher.js +++ b/src/d2m/event-dispatcher.js @@ -288,6 +288,7 @@ module.exports = { if (!guildID) return // channel must have been a DM channel or something const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get() if (!roomID) return // channel wasn't being bridged in the first place + // @ts-ignore await createRoom.unbridgeChannel(channel, guildID) }, @@ -312,10 +313,11 @@ module.exports = { if (!createRoom.existsOrAutocreatable(channel, guild.id)) return // Check that the sending-to room exists or is autocreatable - const {skip} = await speedbump.maybeDoSpeedbump(true, message) - if (skip) return + const {affected, row} = await speedbump.maybeDoSpeedbump(message.channel_id, message.id, message.author.id) + if (affected) return - await sendMessage.sendMessage(message, channel, guild) + // @ts-ignore + await sendMessage.sendMessage(message, channel, guild, row) retrigger.finishedBridging(message.id) }, @@ -333,27 +335,22 @@ module.exports = { if (dUtils.isEphemeralMessage(data)) return // Ephemeral messages are for the eyes of the receiver only! // Edits need to go through the speedbump as well. If the message is delayed but the edit isn't, we don't have anything to edit from. - const {skip, hasCreate} = await speedbump.maybeDoSpeedbump(false, data) - if (skip) return + const {affected, row} = await speedbump.maybeDoSpeedbump(data.channel_id, data.id, data.author.id) + if (affected) return + + // Check that the sending-to room exists, and deal with Eventual Consistency(TM) + if (!await retrigger.waitForMessage(data.id)) return /** @type {DiscordTypes.GatewayMessageCreateDispatchData} */ + // @ts-ignore const message = data const channel = client.channels.get(message.channel_id) if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages. const guild = client.guilds.get(channel.guild_id) assert(guild) - if (!hasCreate) { - // Standard path for most message updates - // Check that the target message already exists, and deal with Eventual Consistency(TM) - if (!await retrigger.waitForMessage(data.id)) return - await retrigger.pauseChanges(message.id, editMessage.editMessage(message, guild)) - } - else { - // Path for edit packets that were speedbumped into the latest copy of a message that needs to be created - // Just pretend to be MESSAGE_CREATE - await sendMessage.sendMessage(message, channel, guild) - } + // @ts-ignore + await retrigger.pauseChanges(message.id, editMessage.editMessage(message, guild, row)) }, /** diff --git a/src/db/migrations/0039-refactor-speedbump.sql b/src/db/migrations/0039-refactor-speedbump.sql deleted file mode 100644 index de739dc..0000000 --- a/src/db/migrations/0039-refactor-speedbump.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE "channel_speedbump" ( - "channel_id" TEXT NOT NULL, - "speedbump_webhook_id" TEXT NOT NULL, - "speedbump_user_id" TEXT NOT NULL, - PRIMARY KEY("channel_id","speedbump_webhook_id"), - FOREIGN KEY("channel_id") REFERENCES "channel_room"("channel_id") -) WITHOUT ROWID; - -INSERT INTO channel_speedbump (channel_id, speedbump_webhook_id, speedbump_user_id) - SELECT channel_id, speedbump_webhook_id, speedbump_id FROM channel_room WHERE speedbump_id IS NOT NULL AND speedbump_webhook_id IS NOT NULL; - -ALTER TABLE channel_room DROP COLUMN speedbump_id; -ALTER TABLE channel_room DROP COLUMN speedbump_webhook_id; - -ALTER TABLE sim_proxy ADD COLUMN proxy_app INTEGER DEFAULT 0; - -COMMIT; diff --git a/src/db/orm-defs.d.ts b/src/db/orm-defs.d.ts index 14f320a..d95bfc3 100644 --- a/src/db/orm-defs.d.ts +++ b/src/db/orm-defs.d.ts @@ -18,17 +18,13 @@ export type Models = { thread_parent: string | null custom_avatar: string | null last_bridged_pin_timestamp: number | null + speedbump_id: string | null + speedbump_webhook_id: string | null speedbump_checked: number | null guild_id: string | null custom_topic: number } - channel_speedbump: { - channel_id: string - speedbump_webhook_id: string - speedbump_user_id: string - } - direct: { mxid: string room_id: string @@ -48,7 +44,6 @@ export type Models = { event_subtype: string | null part: number reaction_part: number - /** 0 = Matrix, 1 = Discord */ source: number } @@ -142,8 +137,6 @@ export type Models = { user_id: string proxy_owner_id: string displayname: string - /** 0 = PluralKit, 1 = /plu/ral */ - proxy_app: number } webhook: { diff --git a/src/matrix/mreq.js b/src/matrix/mreq.js index b6bc5b9..bf238f2 100644 --- a/src/matrix/mreq.js +++ b/src/matrix/mreq.js @@ -9,10 +9,8 @@ const {reg} = require("./read-registration.js") const baseUrl = `${reg.ooye.server_origin}/_matrix` class MatrixServerError extends Error { - /** - * @param {number} httpStatus} - */ - constructor(data, httpStatus, opts = {}) { + /** @param {number} httpStatus} */ + constructor(data, httpStatus, opts) { super(data.error || data.errcode) this.data = data /** @type {number} */ diff --git a/src/types.d.ts b/src/types.d.ts index f65ce59..856ab1c 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -36,7 +36,6 @@ export type AppServiceRegistrationConfig = { web_password: string time_zone?: string receive_presences: boolean - plu_ral_api_key?: string } old_bridge?: { as_token: string @@ -125,28 +124,6 @@ export type PkMessage = { sender: string } -export type PluRalWebhookMessage = { - original_id: string | null - proxy_id: string - author_id: string - channel_id: string - member_id: string - reason: string - webhook_id: string - member: PluRalMember -} - -export type PluRalMember = { - id: string - name: string - pronouns: string - bio: string - birthday: string - color: number | null - avatar_url: string - private: boolean -} - export namespace Event { export type Outer = { type: string diff --git a/src/web/routes/link.test.js b/src/web/routes/link.test.js index 32f89bf..0182093 100644 --- a/src/web/routes/link.test.js +++ b/src/web/routes/link.test.js @@ -73,7 +73,7 @@ test("web link space: check that OOYE is joined", async t => { api: { async joinRoom(roomID) { called++ - throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}, 400) + throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}) } } })) @@ -368,7 +368,7 @@ test("web link room: check that bridge can join room (notices lack of via and as api: { async joinRoom(roomID) { called++ - throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}, 400) + throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}) }, async *generateFullHierarchy(spaceID) { called++ @@ -402,7 +402,7 @@ test("web link room: check that bridge can join room (uses via for join attempt) async joinRoom(roomID, _, via) { called++ t.deepEqual(via, ["cadence.moe", "hashi.re"]) - throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}, 400) + throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}) }, async *generateFullHierarchy(spaceID) { called++ @@ -710,7 +710,7 @@ test("web unlink room: checks that the channel is bridged", async t => { })) t.equal(error.data, "Channel ID 665310973967597573 is not currently bridged") - db.prepare("INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent, custom_avatar, last_bridged_pin_timestamp, speedbump_checked, guild_id, custom_topic) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(row.channel_id, row.room_id, row.name, row.nick, row.thread_parent, row.custom_avatar, row.last_bridged_pin_timestamp, row.speedbump_checked, row.guild_id, row.custom_topic) + db.prepare("INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent, custom_avatar, last_bridged_pin_timestamp, speedbump_id, speedbump_checked, speedbump_webhook_id, guild_id, custom_topic) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(row.channel_id, row.room_id, row.name, row.nick, row.thread_parent, row.custom_avatar, row.last_bridged_pin_timestamp, row.speedbump_id, row.speedbump_checked, row.speedbump_webhook_id, row.guild_id, row.custom_topic) const new_row = db.prepare("SELECT * FROM channel_room WHERE channel_id = '665310973967597573'").get() t.deepEqual(row, new_row) })