diff --git a/src/ClientWidgetApi.ts b/src/ClientWidgetApi.ts index d9fbcbc..a66d2ef 100644 --- a/src/ClientWidgetApi.ts +++ b/src/ClientWidgetApi.ts @@ -73,6 +73,7 @@ import { IUpdateTurnServersRequestData, } from "./interfaces/TurnServerActions"; import { Symbols } from "./Symbols"; +import { ICreateRoomFromWidgetActionRequest, ICreateRoomFromWidgetResponseData } from "./interfaces/CreateRoomAction"; /** * API handler for the client side of widgets. This raises events @@ -564,6 +565,26 @@ export class ClientWidgetApi extends EventEmitter { } } + private async handleCreateRoom(request: ICreateRoomFromWidgetActionRequest) { + if (!this.hasCapability(MatrixCapabilities.MSC3817CreateRoom)) { + return this.transport.reply(request, { + error: { message: "Missing capability" }, + }); + } + + try { + const { roomId } = await this.driver.createRoom(request.data); + return this.transport.reply(request, { + room_id: roomId, + }); + } catch (e) { + console.error("Failed to create a room: ", e); + return this.transport.reply(request, { + error: { message: "Error creating a room" }, + }); + } + } + private handleMessage(ev: CustomEvent) { if (this.isStopped) return; const actionEv = new CustomEvent(`action:${ev.detail.action}`, { @@ -593,6 +614,8 @@ export class ClientWidgetApi extends EventEmitter { return this.handleWatchTurnServers(ev.detail); case WidgetApiFromWidgetAction.UnwatchTurnServers: return this.handleUnwatchTurnServers(ev.detail); + case WidgetApiFromWidgetAction.MSC3817CreateRoom: + return this.handleCreateRoom(ev.detail); default: return this.transport.reply(ev.detail, { error: { diff --git a/src/WidgetApi.ts b/src/WidgetApi.ts index 32311d9..ff7b9c7 100644 --- a/src/WidgetApi.ts +++ b/src/WidgetApi.ts @@ -64,6 +64,8 @@ import { IReadEventFromWidgetRequestData, IReadEventFromWidgetResponseData } fro import { IRoomEvent } from "./interfaces/IRoomEvent"; import { ITurnServer, IUpdateTurnServersRequest } from "./interfaces/TurnServerActions"; import { Symbols } from "./Symbols"; +import { ICreateRoomFromWidgetRequestData, ICreateRoomFromWidgetResponseData } from "./interfaces/CreateRoomAction"; +import { ICreateRoom } from "./interfaces/ICreateRoom"; /** * API handler for widgets. This raises events for each action @@ -550,6 +552,19 @@ export class WidgetApi extends EventEmitter { }); } + /** + * Create a new matrix room + * @param {ICreateRoom} opts The properties of the created room. + * @returns {Promise} Information about the newly created room. + * @deprecated This currently relies on an unstable MSC (MSC3817). + */ + public createRoom(opts: ICreateRoom): Promise { + return this.transport.send( + WidgetApiFromWidgetAction.MSC3817CreateRoom, + { ...opts }, + ); + } + private handleMessage(ev: CustomEvent) { const actionEv = new CustomEvent(`action:${ev.detail.action}`, { detail: ev.detail, diff --git a/src/driver/WidgetDriver.ts b/src/driver/WidgetDriver.ts index 4e1a0fe..dbdce15 100644 --- a/src/driver/WidgetDriver.ts +++ b/src/driver/WidgetDriver.ts @@ -15,6 +15,7 @@ */ import { Capability, IOpenIDCredentials, OpenIDRequestState, SimpleObservable, IRoomEvent, ITurnServer } from ".."; +import { ICreateRoom } from "../interfaces/ICreateRoom"; export interface ISendEventDetails { roomId: string; @@ -140,6 +141,17 @@ export abstract class WidgetDriver { return Promise.resolve([]); } + + /** + * Creates a new room with the given option. The widget API will have already + * verified that the widget is capable of performing this action. + * @param opts The properties of the created room. + * @returns Information about the created room. + */ + public createRoom(opts: ICreateRoom): Promise<{ roomId: string }> { + return Promise.reject(new Error("Failed to override function")); + } + /** * Asks the user for permission to validate their identity through OpenID Connect. The * interface for this function is an observable which accepts the state machine of the diff --git a/src/index.ts b/src/index.ts index 2e160fc..c73151c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ export * from "./transport/ITransport"; export * from "./transport/PostmessageTransport"; // Interfaces and simple models +export * from "./interfaces/ICreateRoom"; export * from "./interfaces/ICustomWidgetData"; export * from "./interfaces/IJitsiWidgetData"; export * from "./interfaces/IStickerpickerWidgetData"; diff --git a/src/interfaces/ApiVersion.ts b/src/interfaces/ApiVersion.ts index 3730cc9..320547f 100644 --- a/src/interfaces/ApiVersion.ts +++ b/src/interfaces/ApiVersion.ts @@ -26,6 +26,7 @@ export enum UnstableApiVersion { MSC2931 = "org.matrix.msc2931", MSC2974 = "org.matrix.msc2974", MSC2876 = "org.matrix.msc2876", + MSC3817 = "org.matrix.msc3817", MSC3819 = "org.matrix.msc3819", MSC3846 = "town.robin.msc3846", } @@ -41,6 +42,7 @@ export const CurrentApiVersions: ApiVersion[] = [ UnstableApiVersion.MSC2931, UnstableApiVersion.MSC2974, UnstableApiVersion.MSC2876, + UnstableApiVersion.MSC3817, UnstableApiVersion.MSC3819, UnstableApiVersion.MSC3846, ]; diff --git a/src/interfaces/Capabilities.ts b/src/interfaces/Capabilities.ts index 0d05443..92eb624 100644 --- a/src/interfaces/Capabilities.ts +++ b/src/interfaces/Capabilities.ts @@ -29,6 +29,10 @@ export enum MatrixCapabilities { * @deprecated It is not recommended to rely on this existing - it can be removed without notice. */ MSC2931Navigate = "org.matrix.msc2931.navigate", + /** + * @deprecated It is not recommended to rely on this existing - it can be removed without notice. + */ + MSC3817CreateRoom = "org.matrix.msc3817.create_room", MSC3846TurnServers = "town.robin.msc3846.turn_servers", } diff --git a/src/interfaces/CreateRoomAction.ts b/src/interfaces/CreateRoomAction.ts new file mode 100644 index 0000000..27c1ee8 --- /dev/null +++ b/src/interfaces/CreateRoomAction.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ICreateRoom } from "./ICreateRoom"; +import { IWidgetApiRequest, IWidgetApiRequestData } from "./IWidgetApiRequest"; +import { IWidgetApiResponseData } from "./IWidgetApiResponse"; +import { WidgetApiFromWidgetAction } from "./WidgetApiAction"; + +export interface ICreateRoomFromWidgetRequestData extends IWidgetApiRequestData, ICreateRoom { + +} + +export interface ICreateRoomFromWidgetActionRequest extends IWidgetApiRequest { + action: WidgetApiFromWidgetAction.MSC3817CreateRoom; + data: ICreateRoomFromWidgetRequestData; +} + +export interface ICreateRoomFromWidgetResponseData extends IWidgetApiResponseData { + room_id: string; // eslint-disable-line camelcase +} + +export interface ICreateRoomFromWidgetActionResponse extends ICreateRoomFromWidgetActionRequest { + response: ICreateRoomFromWidgetResponseData; +} diff --git a/src/interfaces/ICreateRoom.ts b/src/interfaces/ICreateRoom.ts new file mode 100644 index 0000000..6881078 --- /dev/null +++ b/src/interfaces/ICreateRoom.ts @@ -0,0 +1,163 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The body of the `create_room` action. + */ +export interface ICreateRoom { + /** + * Extra keys to be added to the content of the `m.room.create` event. The server will overwrite + * the following keys: `creator`, `room_version`. + */ + creation_content?: Record; // eslint-disable-line camelcase + + /** + * A list of state events to set in the new room. Takes precedence over events sent by `preset`, + * but gets overridden by `name` and `topic`. + */ + initial_state?: PartialStateEvent[]; // eslint-disable-line camelcase + + /** + * A list of user IDs to invite to the room. + */ + invite?: string[]; + + /** + * A list of objects representing third party IDs to invite into the room. + */ + invite_3pid?: Array<{ // eslint-disable-line camelcase + address: string; + id_access_token: string; // eslint-disable-line camelcase + id_server: string; // eslint-disable-line camelcase + medium: string; + }>; + + /** + * The flag makes the server set the `is_direct` flag on the `m.room.member` events sent to the + * users in `invite` and `invite_3pid`. + */ + is_direct?: boolean, // eslint-disable-line camelcase + + /** + * If included, an `m.room.name` event will be sent into the room to indicate the name of the + * room. + */ + name?: string, + + /** + * The power level content to override in the default power level event. This object is applied + * on top of the generated `m.room.power_levels` event content prior to it being sent to the room. + * Defaults to overriding nothing. + */ + power_level_content_override?: PowerLevelsEventContent, // eslint-disable-line camelcase + + /** + * Convenience parameter setting various default state events based on a preset. + * + * If unspecified, the server should use the `visibility` to determine which preset to use. + */ + preset?: 'private_chat' | 'trusted_private_chat' | 'public_chat', + + /** + * The desired room alias **local part**. + */ + room_alias_name?: string, // eslint-disable-line camelcase + + /** + * The room version to set for the room. If not provided, the homeserver is to use its configured + * default. + */ + room_version?: string, // eslint-disable-line camelcase + + /** + * If this is included, an `m.room.topic` event will be sent into the room to indicate the topic + * for the room. + */ + topic?: string, + + /** + * A `public` visibility indicates that the room will be shown in the published room list. A + * `private` visibility will hide the room from the published room list. Rooms default to + * `private` visibility if this key is not included. + */ + visibility?: 'public' | 'private', +} + +/** + * A state event that is used to define the initial state of a newly created room. + */ +export interface PartialStateEvent { + type: string; + content: unknown; + state_key: string; // eslint-disable-line camelcase +} + +/** + * The content definition for m.room.power_levels events + * @category Matrix event contents + * @see PowerLevelsEvent + */ +export interface PowerLevelsEventContent { + /** + * The power level required to ban. Default 50. + */ + ban?: number; + /** + * A map of event types to the power level required to send them. + */ + events?: { + [eventType: string]: number; + }; + /** + * The power level required to send events in the room. Default 50. + */ + events_default?: number; // eslint-disable-line camelcase + /** + * The power level required to invite users to the room. Default 50. + */ + invite?: number; + /** + * The power level required to kick users from the room. Default 50. + */ + kick?: number; + /** + * The power level required to redact other people's events in the room. Default 50. + */ + redact?: number; + /** + * The power level required to send state events in the room. Default 50. + */ + state_default?: number; // eslint-disable-line camelcase + /** + * A map of user IDs to power levels. + */ + users?: { + [userId: string]: number; + }; + /** + * The power level of users not listed in `users`. Default 0. + */ + users_default?: number; // eslint-disable-line camelcase + /** + * Power levels required to send certain kinds of notifications. + */ + notifications?: { + /** + * The power level required to send "@room" notifications. Default 50. + */ + room?: number; + }; +} diff --git a/src/interfaces/WidgetApiAction.ts b/src/interfaces/WidgetApiAction.ts index 6236161..d165cd5 100644 --- a/src/interfaces/WidgetApiAction.ts +++ b/src/interfaces/WidgetApiAction.ts @@ -57,6 +57,11 @@ export enum WidgetApiFromWidgetAction { * @deprecated It is not recommended to rely on this existing - it can be removed without notice. */ MSC2974RenegotiateCapabilities = "org.matrix.msc2974.request_capabilities", + + /** + * @deprecated It is not recommended to rely on this existing - it can be removed without notice. + */ + MSC3817CreateRoom = "org.matrix.msc3817.create_room", } export type WidgetApiAction = WidgetApiToWidgetAction | WidgetApiFromWidgetAction | string;