import { ICreateRoomOpts, ICreateRoomStateEvent, JoinRule, MatrixClient, RestrictedAllowType, Room, } from 'matrix-js-sdk'; import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types'; import { CreateRoomKind } from './CreateRoomKindSelector'; import { RoomType, StateEvent } from '../../../types/matrix/room'; import { getViaServers } from '../../plugins/via-servers'; import { getMxIdServer } from '../../utils/matrix'; export const createRoomCreationContent = ( type: RoomType | undefined, allowFederation: boolean, additionalCreators: string[] | undefined ): object => { const content: Record = {}; if (typeof type === 'string') { content.type = type; } if (allowFederation === false) { content['m.federate'] = false; } if (Array.isArray(additionalCreators)) { content.additional_creators = additionalCreators; } return content; }; export const createRoomJoinRulesState = ( kind: CreateRoomKind, parent: Room | undefined, knock: boolean ) => { let content: RoomJoinRulesEventContent = { join_rule: knock ? JoinRule.Knock : JoinRule.Invite, }; if (kind === CreateRoomKind.Public) { content = { join_rule: JoinRule.Public, }; } if (kind === CreateRoomKind.Restricted && parent) { content = { join_rule: knock ? ('knock_restricted' as JoinRule) : JoinRule.Restricted, allow: [ { type: RestrictedAllowType.RoomMembership, room_id: parent.roomId, }, ], }; } return { type: StateEvent.RoomJoinRules, state_key: '', content, }; }; export const createRoomParentState = (parent: Room) => ({ type: StateEvent.SpaceParent, state_key: parent.roomId, content: { canonical: true, via: getViaServers(parent), }, }); export const createRoomEncryptionState = () => ({ type: 'm.room.encryption', state_key: '', content: { algorithm: 'm.megolm.v1.aes-sha2', }, }); /** * Partial power levels for MatrixRTC call membership (MSC3401). * Do NOT put this in createRoom initial_state — homeservers reject incomplete * m.room.power_levels with 403. Apply after create via sendStateEvent instead, * or rely on TrustedPrivateChat (both users PL 100) for DMs. */ export const createRoomPowerLevelsState = () => ({ type: StateEvent.RoomPowerLevels, state_key: '', content: { events: { 'org.matrix.msc3401.call.member': 0, }, }, }); export type CreateRoomData = { version: string; type?: RoomType; parent?: Room; kind: CreateRoomKind; name: string; topic?: string; aliasLocalPart?: string; encryption?: boolean; knock: boolean; allowFederation: boolean; additionalCreators?: string[]; }; export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promise => { const initialState: ICreateRoomStateEvent[] = []; // initialState.push(createRoomPowerLevelsState()); // Removed: causes 403 when setting permissions before admin if (data.encryption) { initialState.push(createRoomEncryptionState()); } if (data.parent) { initialState.push(createRoomParentState(data.parent)); } initialState.push(createRoomJoinRulesState(data.kind, data.parent, data.knock)); const options: ICreateRoomOpts = { room_version: data.version, name: data.name, topic: data.topic, room_alias_name: data.aliasLocalPart, creation_content: createRoomCreationContent( data.type, data.allowFederation, data.additionalCreators ), initial_state: initialState, }; const result = await mx.createRoom(options); if (data.parent) { await mx.sendStateEvent( data.parent.roomId, StateEvent.SpaceChild as any, { auto_join: false, suggested: false, via: [getMxIdServer(mx.getUserId() ?? '') ?? ''], }, result.room_id ); } return result.room_id; };