diff --git a/src/matrix/api.js b/src/matrix/api.js index 355cc0b..f9a85c0 100644 --- a/src/matrix/api.js +++ b/src/matrix/api.js @@ -22,7 +22,11 @@ function path(p, mxid, otherParams = {}) { const u = new URL(p, "http://localhost") if (mxid) u.searchParams.set("user_id", mxid) for (const entry of Object.entries(otherParams)) { - if (entry[1] != undefined) { + if (Array.isArray(entry[1])) { + for (const element of entry[1]) { + u.searchParams.append(entry[0], element) + } + } else if (entry[1] != undefined) { u.searchParams.set(entry[0], entry[1]) } } @@ -62,11 +66,14 @@ async function createRoom(content) { } /** + * @param {string} roomIDOrAlias + * @param {string?} [mxid] + * @param {string[]?} [via] * @returns {Promise} room ID */ async function joinRoom(roomIDOrAlias, mxid, via) { /** @type {Ty.R.RoomJoined} */ - const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid, via), {}) + const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid, {via}), {}) return root.room_id } diff --git a/src/types.d.ts b/src/types.d.ts index 37da633..aaeed60 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -148,6 +148,14 @@ export namespace Event { prev_content?: any } + export type Outer_StrippedChildStateEvent = { + type: string + state_key: string + sender: string + origin_server_ts: number + content: any + } + export type M_Room_Message = { msgtype: "m.text" | "m.emote" body: string @@ -344,7 +352,7 @@ export namespace R { export type Hierarchy = { avatar_url?: string canonical_alias?: string - children_state: {} + children_state: Event.Outer_StrippedChildStateEvent[] guest_can_join: boolean join_rule?: string name?: string diff --git a/src/web/routes/link.js b/src/web/routes/link.js index 7654adf..f85e66d 100644 --- a/src/web/routes/link.js +++ b/src/web/routes/link.js @@ -134,32 +134,34 @@ as.router.post("/api/link", defineEventHandler(async event => { if (row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${row.channel_id} or room ID ${parsedBody.matrix} are already bridged and cannot be reused`}) // Check room is part of the guild's space - let found = false - let via = undefined + let foundRoom = false + /** @type {string[]?} */ + let foundVia = null for await (const room of api.generateFullHierarchy(spaceID)) { - if (via === undefined && room.room_type === "m.space") { - for (state of room.children_state) { - if (state.state_key === parsedBody.matrix){ - via = {via: state.content.via} - if (found === true) - break - } + // When finding a space during iteration, look at space's children state, because we need a `via` to join the room (when we find it later) + for (const state of room.children_state) { + if (state.type === "m.space.child" && state.state_key === parsedBody.matrix) { + foundVia = state.content.via } } - if (!found && room.room_id === parsedBody.matrix && !room.room_type) { - found = true - if (via !== undefined) - break + // When finding a room during iteration, see if it was the requested room (to confirm that the room is in the space) + if (room.room_id === parsedBody.matrix && !room.room_type) { + foundRoom = true } + + if (foundRoom && foundVia) break } - if (!found) throw createError({status: 400, message: "Bad Request", data: "Matrix room needs to be part of the bridged space"}) + if (!foundRoom) throw createError({status: 400, message: "Bad Request", data: "Matrix room needs to be part of the bridged space"}) // Check room exists and bridge is joined try { - await api.joinRoom(parsedBody.matrix, null, via ?? {}) + await api.joinRoom(parsedBody.matrix, null, foundVia) } catch (e) { - throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}${via === null ? " (hint: couln't find a \"via\" in the space children_state for this room in order to help joining this room)" : ""}`}) + if (!foundVia) { + throw createError({status: 403, message: "Unable To Join", data: `Unable to join the requested Matrix room. Please invite the bridge to the room and try again. (Server said: ${e.errcode} - ${e.message})`}) + } + throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) } // Check bridge has PL 100