diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index d8bab51..638d89c 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -12,6 +12,8 @@ const file = sync.require("../../matrix/file") const api = sync.require("../../matrix/api") /** @type {import("../../matrix/kstate")} */ const ks = sync.require("../../matrix/kstate") +/** @type {import("./create-space")}) */ +const createSpace = sync.require("./create-space") // watch out for the require loop /** @type {Map>} channel ID -> Promise */ const inflightRoomCreate = new Map() @@ -61,12 +63,12 @@ function convertNameAndTopic(channel, guild, customName) { } /** - * Async because it may upload the guild icon to mxc. + * Async because it may create the guild and/or upload the guild icon to mxc. * @param {DiscordTypes.APIGuildTextChannel | DiscordTypes.APIThreadChannel} channel * @param {DiscordTypes.APIGuild} guild */ async function channelToKState(channel, guild) { - const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id) + const spaceID = await createSpace.ensureSpace(guild.id) assert.ok(typeof spaceID === "string") const row = db.prepare("SELECT nick, custom_avatar FROM channel_room WHERE channel_id = ?").get(channel.id) diff --git a/d2m/actions/create-space.js b/d2m/actions/create-space.js index 80e49dd..102b2a0 100644 --- a/d2m/actions/create-space.js +++ b/d2m/actions/create-space.js @@ -15,6 +15,9 @@ const createRoom = sync.require("./create-room") /** @type {import("../../matrix/kstate")} */ const ks = sync.require("../../matrix/kstate") +/** @type {Map>} guild ID -> Promise */ +const inflightSpaceCreate = new Map() + /** * @param {import("discord-api-types/v10").RESTGetAPIGuildResult} guild * @param {any} kstate @@ -68,24 +71,43 @@ async function guildToKState(guild) { return guildKState } -/** Efficiently update space name, space avatar, and child room avatars. */ -async function syncSpace(guildID) { +/** + * @param {string} guildID + * @param {boolean} shouldActuallySync false if just need to ensure nspace exists (which is a quick database check), + * true if also want to efficiently sync space name, space avatar, and child room avatars + * @returns {Promise} room ID + */ +async function _syncSpace(guildID, shouldActuallySync) { /** @ts-ignore @type {DiscordTypes.APIGuild} */ const guild = discord.guilds.get(guildID) assert.ok(guild) + if (inflightSpaceCreate.has(guildID)) { + await inflightSpaceCreate.get(guildID) // just waiting, and then doing a new db query afterwards, is the simplest way of doing it + } + /** @type {string?} */ const spaceID = db.prepare("SELECT space_id from guild_space WHERE guild_id = ?").pluck().get(guildID) - const guildKState = await guildToKState(guild) - if (!spaceID) { - const spaceID = await createSpace(guild, guildKState) - return spaceID // Naturally, the newly created space is already up to date, so we can always skip syncing here. + const creation = (async () => { + const guildKState = await guildToKState(guild) + const spaceID = await createSpace(guild, guildKState) + inflightSpaceCreate.delete(guildID) + return spaceID + })() + inflightSpaceCreate.set(guildID, creation) + return creation // Naturally, the newly created space is already up to date, so we can always skip syncing here. + } + + if (!shouldActuallySync) { + return spaceID // only need to ensure space exists, and it does. return the space ID } console.log(`[space sync] to matrix: ${guild.name}`) + const guildKState = await guildToKState(guild) // calling this in both branches because we don't want to calculate this if not syncing + // sync guild state to space const spaceKState = await createRoom.roomToKState(spaceID) const spaceDiff = ks.diffKState(spaceKState, guildKState) @@ -113,6 +135,16 @@ async function syncSpace(guildID) { return spaceID } +/** Ensures the space exists. If it doesn't, creates the space with an accurate initial state. */ +function ensureSpace(guildID) { + return _syncSpace(guildID, false) +} + +/** Actually syncs. Efficiently updates the space name, space avatar, and child room avatars. */ +function syncSpace(guildID) { + return _syncSpace(guildID, true) +} + /** * Inefficiently force the space and its existing child rooms to be fully updated. * Should not need to be called as part of the bridge's normal operation. @@ -157,6 +189,7 @@ async function syncSpaceFully(guildID) { } module.exports.createSpace = createSpace +module.exports.ensureSpace = ensureSpace module.exports.syncSpace = syncSpace module.exports.syncSpaceFully = syncSpaceFully module.exports.guildToKState = guildToKState diff --git a/d2m/actions/register-user.test.js b/d2m/actions/register-user.test.js index 74818ea..5cee80b 100644 --- a/d2m/actions/register-user.test.js +++ b/d2m/actions/register-user.test.js @@ -1,4 +1,3 @@ -const {channelToKState} = require("./create-room") const {_memberToStateContent} = require("./register-user") const {test} = require("supertape") const testData = require("../../test/data")