You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-07 23:02:56 +03:00
Improve types for sendEvent
(#4108)
This commit is contained in:
committed by
GitHub
parent
85a55c79cd
commit
97844f0e47
@@ -50,6 +50,7 @@ import {
|
||||
ClientEvent,
|
||||
createClient,
|
||||
CryptoEvent,
|
||||
HistoryVisibility,
|
||||
IClaimOTKsResult,
|
||||
IContent,
|
||||
IDownloadKeyResult,
|
||||
@@ -59,11 +60,11 @@ import {
|
||||
MatrixClient,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
MsgType,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
RoomMember,
|
||||
RoomStateEvent,
|
||||
HistoryVisibility,
|
||||
} from "../../../src/matrix";
|
||||
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
||||
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
|
||||
@@ -1925,7 +1926,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
expectAliceKeyQuery({ device_keys: { "@other:user": {} }, failures: {} });
|
||||
aliceClient.on(RoomStateEvent.NewMember, (_e, _s, member: RoomMember) => {
|
||||
if (member.userId == "@other:user") {
|
||||
aliceClient.sendMessage(testRoomId, { msgtype: "m.text", body: "Hello, World" });
|
||||
aliceClient.sendMessage(testRoomId, { msgtype: MsgType.Text, body: "Hello, World" });
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -34,7 +34,7 @@ import { logger } from "../../../src/logger";
|
||||
import * as testUtils from "../../test-utils/test-utils";
|
||||
import { TestClient } from "../../TestClient";
|
||||
import { CRYPTO_ENABLED, IClaimKeysRequest, IQueryKeysRequest, IUploadKeysRequest } from "../../../src/client";
|
||||
import { ClientEvent, IContent, ISendEventResponse, MatrixClient, MatrixEvent } from "../../../src/matrix";
|
||||
import { ClientEvent, IContent, ISendEventResponse, MatrixClient, MatrixEvent, MsgType } from "../../../src/matrix";
|
||||
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
||||
import { KnownMembership } from "../../../src/@types/membership";
|
||||
|
||||
@@ -217,7 +217,7 @@ async function expectBobSendMessageRequest(): Promise<OlmPayload> {
|
||||
}
|
||||
|
||||
function sendMessage(client: MatrixClient): Promise<ISendEventResponse> {
|
||||
return client.sendMessage(roomId, { msgtype: "m.text", body: "Hello, World" });
|
||||
return client.sendMessage(roomId, { msgtype: MsgType.Text, body: "Hello, World" });
|
||||
}
|
||||
|
||||
async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]): Promise<IContent> {
|
||||
|
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
import HttpBackend from "matrix-mock-request";
|
||||
|
||||
import { EventStatus, RoomEvent, MatrixClient, MatrixScheduler } from "../../src/matrix";
|
||||
import { EventStatus, MatrixClient, MatrixScheduler, MsgType, RoomEvent } from "../../src/matrix";
|
||||
import { Room } from "../../src/models/room";
|
||||
import { TestClient } from "../TestClient";
|
||||
|
||||
@@ -60,7 +60,7 @@ describe("MatrixClient retrying", function () {
|
||||
// send a couple of events; the second will be queued
|
||||
const p1 = client!
|
||||
.sendMessage(roomId, {
|
||||
msgtype: "m.text",
|
||||
msgtype: MsgType.Text,
|
||||
body: "m1",
|
||||
})
|
||||
.then(
|
||||
@@ -77,7 +77,7 @@ describe("MatrixClient retrying", function () {
|
||||
// never gets resolved.
|
||||
// https://github.com/matrix-org/matrix-js-sdk/issues/496
|
||||
client!.sendMessage(roomId, {
|
||||
msgtype: "m.text",
|
||||
msgtype: MsgType.Text,
|
||||
body: "m2",
|
||||
});
|
||||
|
||||
|
@@ -152,7 +152,7 @@ describe("MatrixClient syncing", () => {
|
||||
await client!.sendEvent(roomId, EventType.Reaction, {
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Annotation,
|
||||
event_id: threadReply.getId(),
|
||||
event_id: threadReply.getId()!,
|
||||
key: "",
|
||||
},
|
||||
});
|
||||
|
@@ -81,6 +81,12 @@ declare module "../../src/types" {
|
||||
hello: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface TimelineEvents {
|
||||
"org.matrix.rageshake_request": {
|
||||
request_id: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
describe("RoomWidgetClient", () => {
|
||||
|
@@ -22,6 +22,7 @@ import { Filter } from "../../src/filter";
|
||||
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE } from "../../src/models/MSC3089TreeSpace";
|
||||
import {
|
||||
EventType,
|
||||
MsgType,
|
||||
RelationType,
|
||||
RoomCreateTypeField,
|
||||
RoomType,
|
||||
@@ -73,6 +74,7 @@ import { StubStore } from "../../src/store/stub";
|
||||
import { SecretStorageKeyDescriptionAesV1, ServerSideSecretStorageImpl } from "../../src/secret-storage";
|
||||
import { CryptoBackend } from "../../src/common-crypto/CryptoBackend";
|
||||
import { KnownMembership } from "../../src/@types/membership";
|
||||
import { RoomMessageEventContent } from "../../src/@types/events";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@@ -567,7 +569,7 @@ describe("MatrixClient", function () {
|
||||
describe("sendEvent", () => {
|
||||
const roomId = "!room:example.org";
|
||||
const body = "This is the body";
|
||||
const content = { body };
|
||||
const content = { body, msgtype: MsgType.Text } satisfies RoomMessageEventContent;
|
||||
|
||||
it("overload without threadId works", async () => {
|
||||
const eventId = "$eventId:example.org";
|
||||
@@ -662,12 +664,13 @@ describe("MatrixClient", function () {
|
||||
|
||||
const content = {
|
||||
body,
|
||||
"msgtype": MsgType.Text,
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: "$other:event",
|
||||
},
|
||||
},
|
||||
};
|
||||
} satisfies RoomMessageEventContent;
|
||||
|
||||
const room = new Room(roomId, client, userId);
|
||||
mocked(store.getRoom).mockReturnValue(room);
|
||||
|
@@ -26,6 +26,7 @@ import {
|
||||
import { DEFAULT_ALPHABET } from "../../../src/utils";
|
||||
import { MatrixError } from "../../../src/http-api";
|
||||
import { KnownMembership } from "../../../src/@types/membership";
|
||||
import { EncryptedFile } from "../../../src/@types/media";
|
||||
|
||||
describe("MSC3089TreeSpace", () => {
|
||||
let client: MatrixClient;
|
||||
@@ -947,7 +948,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
const fileInfo = {
|
||||
mimetype: "text/plain",
|
||||
// other fields as required by encryption, but ignored here
|
||||
};
|
||||
} as unknown as EncryptedFile;
|
||||
const fileEventId = "$file";
|
||||
const fileName = "My File.txt";
|
||||
const fileContents = "This is a test file";
|
||||
@@ -1007,7 +1008,7 @@ describe("MSC3089TreeSpace", () => {
|
||||
const fileInfo = {
|
||||
mimetype: "text/plain",
|
||||
// other fields as required by encryption, but ignored here
|
||||
};
|
||||
} as unknown as EncryptedFile;
|
||||
const fileEventId = "$file";
|
||||
const fileName = "My File.txt";
|
||||
const fileContents = "This is a test file";
|
||||
|
@@ -41,8 +41,23 @@ import {
|
||||
IGroupCallRoomState,
|
||||
} from "../webrtc/groupCall";
|
||||
import { MSC3089EventContent } from "../models/MSC3089Branch";
|
||||
import { M_BEACON_INFO, MBeaconInfoEventContent } from "./beacon";
|
||||
import { M_BEACON, M_BEACON_INFO, MBeaconEventContent, MBeaconInfoEventContent } from "./beacon";
|
||||
import { XOR } from "./common";
|
||||
import { ReactionEventContent, RoomMessageEventContent, StickerEventContent } from "./events";
|
||||
import {
|
||||
MCallAnswer,
|
||||
MCallBase,
|
||||
MCallCandidates,
|
||||
MCallHangupReject,
|
||||
MCallInviteNegotiate,
|
||||
MCallReplacesEvent,
|
||||
MCallSelectAnswer,
|
||||
SDPStreamMetadata,
|
||||
SDPStreamMetadataKey,
|
||||
} from "../webrtc/callEventTypes";
|
||||
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types";
|
||||
import { EncryptedFile } from "./media";
|
||||
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls";
|
||||
|
||||
export enum EventType {
|
||||
// Room state events
|
||||
@@ -283,21 +298,37 @@ export const LOCAL_NOTIFICATION_SETTINGS_PREFIX = new UnstableValue(
|
||||
*/
|
||||
export const UNSIGNED_THREAD_ID_FIELD = new UnstableValue("thread_id", "org.matrix.msc4023.thread_id");
|
||||
|
||||
export interface IEncryptedFile {
|
||||
url: string;
|
||||
mimetype?: string;
|
||||
key: {
|
||||
alg: string;
|
||||
key_ops: string[]; // eslint-disable-line camelcase
|
||||
kty: string;
|
||||
k: string;
|
||||
ext: boolean;
|
||||
};
|
||||
iv: string;
|
||||
hashes: { [alg: string]: string };
|
||||
v: string;
|
||||
/**
|
||||
* @deprecated in favour of {@link EncryptedFile}
|
||||
*/
|
||||
export type IEncryptedFile = EncryptedFile;
|
||||
|
||||
/**
|
||||
* Mapped type from event type to content type for all specified non-state room events.
|
||||
*/
|
||||
export interface TimelineEvents {
|
||||
[EventType.RoomMessage]: RoomMessageEventContent;
|
||||
[EventType.Sticker]: StickerEventContent;
|
||||
[EventType.Reaction]: ReactionEventContent;
|
||||
[EventType.CallReplaces]: MCallReplacesEvent;
|
||||
[EventType.CallAnswer]: MCallAnswer;
|
||||
[EventType.CallSelectAnswer]: MCallSelectAnswer;
|
||||
[EventType.CallNegotiate]: Omit<MCallInviteNegotiate, "offer">;
|
||||
[EventType.CallInvite]: MCallInviteNegotiate;
|
||||
[EventType.CallCandidates]: MCallCandidates;
|
||||
[EventType.CallHangup]: MCallHangupReject;
|
||||
[EventType.CallReject]: MCallHangupReject;
|
||||
[EventType.CallSDPStreamMetadataChangedPrefix]: MCallBase & { [SDPStreamMetadataKey]: SDPStreamMetadata };
|
||||
[EventType.CallEncryptionKeysPrefix]: EncryptionKeysEventContent;
|
||||
[EventType.CallNotify]: ICallNotifyContent;
|
||||
[M_BEACON.name]: MBeaconEventContent;
|
||||
[M_POLL_START.name]: PollStartEventContent;
|
||||
[M_POLL_END.name]: PollEndEventContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapped type from event type to content type for all specified room state events.
|
||||
*/
|
||||
export interface StateEvents {
|
||||
[EventType.RoomCanonicalAlias]: RoomCanonicalAliasEventContent;
|
||||
[EventType.RoomCreate]: RoomCreateEventContent;
|
||||
|
119
src/@types/events.ts
Normal file
119
src/@types/events.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2024 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 { MsgType, RelationType } from "./event";
|
||||
import { FileInfo, ImageInfo, MediaEventContent } from "./media";
|
||||
import { XOR } from "./common";
|
||||
|
||||
interface BaseTimelineEvent {
|
||||
"body": string;
|
||||
"m.mentions"?: {
|
||||
user_ids?: string[];
|
||||
room?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface ReplyEvent {
|
||||
"m.relates_to"?: {
|
||||
"m.in_reply_to"?: {
|
||||
event_id: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface NoRelationEvent {
|
||||
"m.new_content"?: never;
|
||||
"m.relates_to"?: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial content format of timeline events with rel_type `m.replace`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#event-replacements
|
||||
*/
|
||||
export interface ReplacementEvent<T> {
|
||||
"m.new_content": T;
|
||||
"m.relates_to": {
|
||||
event_id: string;
|
||||
rel_type: RelationType.Replace;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial content format of timeline events with rel_type other than `m.replace`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#forming-relationships-between-events
|
||||
*/
|
||||
export interface RelationEvent {
|
||||
"m.new_content"?: never;
|
||||
"m.relates_to": {
|
||||
event_id: string;
|
||||
rel_type: Exclude<RelationType, RelationType.Replace>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Content format of timeline events with type `m.room.message` and `msgtype` `m.text`, `m.emote`, or `m.notice`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#mroommessage
|
||||
*/
|
||||
export interface RoomMessageTextEventContent extends BaseTimelineEvent {
|
||||
msgtype: MsgType.Text | MsgType.Emote | MsgType.Notice;
|
||||
format?: "org.matrix.custom.html";
|
||||
formatted_body?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content format of timeline events with type `m.room.message` and `msgtype` `m.location`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#mlocation
|
||||
*/
|
||||
export interface RoomMessageLocationEventContent extends BaseTimelineEvent {
|
||||
body: string;
|
||||
geo_uri: string;
|
||||
info: Pick<FileInfo, "thumbnail_info" | "thumbnail_file" | "thumbnail_url">;
|
||||
msgtype: MsgType.Location;
|
||||
}
|
||||
|
||||
type MessageEventContent = RoomMessageTextEventContent | RoomMessageLocationEventContent | MediaEventContent;
|
||||
|
||||
export type RoomMessageEventContent = BaseTimelineEvent &
|
||||
XOR<XOR<ReplacementEvent<MessageEventContent>, RelationEvent>, XOR<ReplyEvent, NoRelationEvent>> &
|
||||
MessageEventContent;
|
||||
|
||||
/**
|
||||
* Content format of timeline events with type `m.sticker`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#msticker
|
||||
*/
|
||||
export interface StickerEventContent extends BaseTimelineEvent {
|
||||
body: string;
|
||||
info: ImageInfo;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content format of timeline events with type `m.reaction`
|
||||
*
|
||||
* @see https://spec.matrix.org/v1.9/client-server-api/#mreaction
|
||||
*/
|
||||
export interface ReactionEventContent {
|
||||
"m.relates_to": {
|
||||
event_id: string;
|
||||
key: string;
|
||||
rel_type: RelationType.Annotation;
|
||||
};
|
||||
}
|
@@ -145,6 +145,7 @@ import {
|
||||
RoomCreateTypeField,
|
||||
RoomType,
|
||||
StateEvents,
|
||||
TimelineEvents,
|
||||
UNSTABLE_MSC3088_ENABLED,
|
||||
UNSTABLE_MSC3088_PURPOSE,
|
||||
UNSTABLE_MSC3089_TREE_SUBTYPE,
|
||||
@@ -223,6 +224,7 @@ import { RegisterRequest, RegisterResponse } from "./@types/registration";
|
||||
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager";
|
||||
import { getRelationsThreadFilter } from "./thread-utils";
|
||||
import { KnownMembership, Membership } from "./@types/membership";
|
||||
import { RoomMessageEventContent, StickerEventContent } from "./@types/events";
|
||||
import { ImageInfo } from "./@types/media";
|
||||
|
||||
export type Store = IStore;
|
||||
@@ -4551,12 +4553,17 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
return this.sendStateEvent(roomId, M_BEACON_INFO.name, beaconInfoContent, this.getUserId()!);
|
||||
}
|
||||
|
||||
public sendEvent(roomId: string, eventType: string, content: IContent, txnId?: string): Promise<ISendEventResponse>;
|
||||
public sendEvent(
|
||||
public sendEvent<K extends keyof TimelineEvents>(
|
||||
roomId: string,
|
||||
eventType: K,
|
||||
content: TimelineEvents[K],
|
||||
txnId?: string,
|
||||
): Promise<ISendEventResponse>;
|
||||
public sendEvent<K extends keyof TimelineEvents>(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventType: string,
|
||||
content: IContent,
|
||||
eventType: K,
|
||||
content: TimelineEvents[K],
|
||||
txnId?: string,
|
||||
): Promise<ISendEventResponse>;
|
||||
public sendEvent(
|
||||
@@ -4943,27 +4950,27 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
* @returns Promise which resolves: to an ISendEventResponse object
|
||||
* @returns Rejects: with an error response.
|
||||
*/
|
||||
public sendMessage(roomId: string, content: IContent, txnId?: string): Promise<ISendEventResponse>;
|
||||
public sendMessage(roomId: string, content: RoomMessageEventContent, txnId?: string): Promise<ISendEventResponse>;
|
||||
public sendMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
content: IContent,
|
||||
content: RoomMessageEventContent,
|
||||
txnId?: string,
|
||||
): Promise<ISendEventResponse>;
|
||||
public sendMessage(
|
||||
roomId: string,
|
||||
threadId: string | null | IContent,
|
||||
content?: IContent | string,
|
||||
threadId: string | null | RoomMessageEventContent,
|
||||
content?: RoomMessageEventContent | string,
|
||||
txnId?: string,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (typeof threadId !== "string" && threadId !== null) {
|
||||
txnId = content as string;
|
||||
content = threadId as IContent;
|
||||
content = threadId as RoomMessageEventContent;
|
||||
threadId = null;
|
||||
}
|
||||
|
||||
const eventType: string = EventType.RoomMessage;
|
||||
const sendContent: IContent = content as IContent;
|
||||
const eventType = EventType.RoomMessage;
|
||||
const sendContent = content as RoomMessageEventContent;
|
||||
|
||||
return this.sendEvent(roomId, threadId as string | null, eventType, sendContent, txnId);
|
||||
}
|
||||
@@ -5076,10 +5083,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
const content = {
|
||||
msgtype: MsgType.Image,
|
||||
url: url,
|
||||
info: info,
|
||||
url: url as string,
|
||||
info: info as ImageInfo,
|
||||
body: text,
|
||||
};
|
||||
} satisfies RoomMessageEventContent;
|
||||
return this.sendMessage(roomId, threadId, content);
|
||||
}
|
||||
|
||||
@@ -5114,10 +5121,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
threadId = null;
|
||||
}
|
||||
const content = {
|
||||
url: url,
|
||||
info: info,
|
||||
url: url as string,
|
||||
info: info as ImageInfo,
|
||||
body: text,
|
||||
};
|
||||
} satisfies StickerEventContent;
|
||||
|
||||
return this.sendEvent(roomId, threadId, EventType.Sticker, content);
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ import {
|
||||
LegacyLocationEventContent,
|
||||
} from "./@types/location";
|
||||
import { MRoomTopicEventContent, MTopicContent, M_TOPIC } from "./@types/topic";
|
||||
import { IContent } from "./models/event";
|
||||
import { RoomMessageEventContent } from "./@types/events";
|
||||
|
||||
/**
|
||||
* Generates the content for a HTML Message event
|
||||
@@ -38,7 +38,7 @@ import { IContent } from "./models/event";
|
||||
* @param htmlBody - the HTML representation of the message
|
||||
* @returns
|
||||
*/
|
||||
export function makeHtmlMessage(body: string, htmlBody: string): IContent {
|
||||
export function makeHtmlMessage(body: string, htmlBody: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Text,
|
||||
format: "org.matrix.custom.html",
|
||||
@@ -53,7 +53,7 @@ export function makeHtmlMessage(body: string, htmlBody: string): IContent {
|
||||
* @param htmlBody - the HTML representation of the notice
|
||||
* @returns
|
||||
*/
|
||||
export function makeHtmlNotice(body: string, htmlBody: string): IContent {
|
||||
export function makeHtmlNotice(body: string, htmlBody: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Notice,
|
||||
format: "org.matrix.custom.html",
|
||||
@@ -68,7 +68,7 @@ export function makeHtmlNotice(body: string, htmlBody: string): IContent {
|
||||
* @param htmlBody - the HTML representation of the emote
|
||||
* @returns
|
||||
*/
|
||||
export function makeHtmlEmote(body: string, htmlBody: string): IContent {
|
||||
export function makeHtmlEmote(body: string, htmlBody: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Emote,
|
||||
format: "org.matrix.custom.html",
|
||||
@@ -82,7 +82,7 @@ export function makeHtmlEmote(body: string, htmlBody: string): IContent {
|
||||
* @param body - the plaintext body of the emote
|
||||
* @returns
|
||||
*/
|
||||
export function makeTextMessage(body: string): IContent {
|
||||
export function makeTextMessage(body: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Text,
|
||||
body: body,
|
||||
@@ -94,7 +94,7 @@ export function makeTextMessage(body: string): IContent {
|
||||
* @param body - the plaintext body of the notice
|
||||
* @returns
|
||||
*/
|
||||
export function makeNotice(body: string): IContent {
|
||||
export function makeNotice(body: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Notice,
|
||||
body: body,
|
||||
@@ -106,7 +106,7 @@ export function makeNotice(body: string): IContent {
|
||||
* @param body - the plaintext body of the emote
|
||||
* @returns
|
||||
*/
|
||||
export function makeEmoteMessage(body: string): IContent {
|
||||
export function makeEmoteMessage(body: string): RoomMessageEventContent {
|
||||
return {
|
||||
msgtype: MsgType.Emote,
|
||||
body: body,
|
||||
|
@@ -18,7 +18,7 @@ limitations under the License.
|
||||
import { VerificationRequest, REQUEST_TYPE, READY_TYPE, START_TYPE } from "./VerificationRequest";
|
||||
import { logger } from "../../../logger";
|
||||
import { IVerificationChannel } from "./Channel";
|
||||
import { EventType } from "../../../@types/event";
|
||||
import { EventType, TimelineEvents } from "../../../@types/event";
|
||||
import { MatrixClient } from "../../../client";
|
||||
import { MatrixEvent } from "../../../models/event";
|
||||
import { IRequestsMap } from "../..";
|
||||
@@ -299,7 +299,11 @@ export class InRoomChannel implements IVerificationChannel {
|
||||
if (type === REQUEST_TYPE) {
|
||||
sendType = MESSAGE_TYPE;
|
||||
}
|
||||
const response = await this.client.sendEvent(this.roomId, sendType, content);
|
||||
const response = await this.client.sendEvent(
|
||||
this.roomId,
|
||||
sendType as keyof TimelineEvents,
|
||||
content as TimelineEvents[keyof TimelineEvents],
|
||||
);
|
||||
if (type === REQUEST_TYPE) {
|
||||
this.requestEventId = response.event_id;
|
||||
}
|
||||
|
@@ -15,12 +15,20 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient } from "../client";
|
||||
import { IEncryptedFile, RelationType, UNSTABLE_MSC3089_BRANCH } from "../@types/event";
|
||||
import { RelationType, UNSTABLE_MSC3089_BRANCH } from "../@types/event";
|
||||
import { IContent, MatrixEvent } from "./event";
|
||||
import { MSC3089TreeSpace } from "./MSC3089TreeSpace";
|
||||
import { EventTimeline } from "./event-timeline";
|
||||
import { FileType } from "../http-api";
|
||||
import type { ISendEventResponse } from "../@types/requests";
|
||||
import { EncryptedFile } from "../@types/media";
|
||||
|
||||
export interface MSC3089EventContent {
|
||||
active?: boolean;
|
||||
name?: string;
|
||||
locked?: boolean;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
export interface MSC3089EventContent {
|
||||
active?: boolean;
|
||||
@@ -138,7 +146,7 @@ export class MSC3089Branch {
|
||||
* Gets information about the file needed to download it.
|
||||
* @returns Information about the file.
|
||||
*/
|
||||
public async getFileInfo(): Promise<{ info: IEncryptedFile; httpUrl: string }> {
|
||||
public async getFileInfo(): Promise<{ info: EncryptedFile; httpUrl: string }> {
|
||||
const event = await this.getFileEvent();
|
||||
|
||||
const file = event.getOriginalContent()["file"];
|
||||
@@ -186,7 +194,7 @@ export class MSC3089Branch {
|
||||
public async createNewVersion(
|
||||
name: string,
|
||||
encryptedContents: FileType,
|
||||
info: Partial<IEncryptedFile>,
|
||||
info: EncryptedFile,
|
||||
additionalContent?: IContent,
|
||||
): Promise<ISendEventResponse> {
|
||||
const fileEventResponse = await this.directory.createFile(name, encryptedContents, info, {
|
||||
|
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
import promiseRetry from "p-retry";
|
||||
|
||||
import { MatrixClient } from "../client";
|
||||
import { EventType, IEncryptedFile, MsgType, UNSTABLE_MSC3089_BRANCH, UNSTABLE_MSC3089_LEAF } from "../@types/event";
|
||||
import { EventType, MsgType, UNSTABLE_MSC3089_BRANCH, UNSTABLE_MSC3089_LEAF } from "../@types/event";
|
||||
import { Room } from "./room";
|
||||
import { logger } from "../logger";
|
||||
import { IContent, MatrixEvent } from "./event";
|
||||
@@ -35,6 +35,7 @@ import { ISendEventResponse } from "../@types/requests";
|
||||
import { FileType } from "../http-api";
|
||||
import { KnownMembership } from "../@types/membership";
|
||||
import { RoomPowerLevelsEventContent, SpaceChildEventContent } from "../@types/state_events";
|
||||
import { EncryptedFile, FileContent } from "../@types/media";
|
||||
|
||||
/**
|
||||
* The recommended defaults for a tree space's power levels. Note that this
|
||||
@@ -79,6 +80,12 @@ export enum TreePermissions {
|
||||
Owner = "owner", // "Admin" or PL100
|
||||
}
|
||||
|
||||
declare module "../@types/media" {
|
||||
interface FileContent {
|
||||
[UNSTABLE_MSC3089_LEAF.name]?: {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089)
|
||||
* file tree Space. Note that this is UNSTABLE and subject to breaking changes
|
||||
@@ -502,7 +509,7 @@ export class MSC3089TreeSpace {
|
||||
public async createFile(
|
||||
name: string,
|
||||
encryptedContents: FileType,
|
||||
info: Partial<IEncryptedFile>,
|
||||
info: EncryptedFile,
|
||||
additionalContent?: IContent,
|
||||
): Promise<ISendEventResponse> {
|
||||
const { content_uri: mxc } = await this.client.uploadContent(encryptedContents, {
|
||||
@@ -510,7 +517,7 @@ export class MSC3089TreeSpace {
|
||||
});
|
||||
info.url = mxc;
|
||||
|
||||
const fileContent = {
|
||||
const fileContent: FileContent = {
|
||||
msgtype: MsgType.File,
|
||||
body: name,
|
||||
url: mxc,
|
||||
@@ -529,7 +536,7 @@ export class MSC3089TreeSpace {
|
||||
...additionalContent,
|
||||
...fileContent,
|
||||
[UNSTABLE_MSC3089_LEAF.name]: {},
|
||||
});
|
||||
} as FileContent);
|
||||
|
||||
await this.client.sendStateEvent(
|
||||
this.roomId,
|
||||
|
@@ -24,6 +24,7 @@ limitations under the License.
|
||||
export type * from "./@types/media";
|
||||
export * from "./@types/membership";
|
||||
export type * from "./@types/event";
|
||||
export type * from "./@types/events";
|
||||
export type * from "./@types/state_events";
|
||||
|
||||
/** The different methods for device and user verification */
|
||||
|
@@ -26,8 +26,8 @@ import { parse as parseSdp, write as writeSdp } from "sdp-transform";
|
||||
|
||||
import { logger } from "../logger";
|
||||
import { checkObjectHasKeys, isNullOrUndefined, recursivelyAssign } from "../utils";
|
||||
import { IContent, MatrixEvent } from "../models/event";
|
||||
import { EventType, ToDeviceMessageId } from "../@types/event";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { EventType, TimelineEvents, ToDeviceMessageId } from "../@types/event";
|
||||
import { RoomMember } from "../models/room-member";
|
||||
import { randomString } from "../randomstring";
|
||||
import {
|
||||
@@ -293,13 +293,24 @@ function getCodecParamMods(isPtt: boolean): CodecParamsMod[] {
|
||||
return mods;
|
||||
}
|
||||
|
||||
type CallEventType =
|
||||
| EventType.CallReplaces
|
||||
| EventType.CallAnswer
|
||||
| EventType.CallSelectAnswer
|
||||
| EventType.CallNegotiate
|
||||
| EventType.CallInvite
|
||||
| EventType.CallCandidates
|
||||
| EventType.CallHangup
|
||||
| EventType.CallReject
|
||||
| EventType.CallSDPStreamMetadataChangedPrefix;
|
||||
|
||||
export interface VoipEvent {
|
||||
type: "toDevice" | "sendEvent";
|
||||
eventType: string;
|
||||
userId?: string;
|
||||
opponentDeviceId?: string;
|
||||
roomId?: string;
|
||||
content: Record<string, unknown>;
|
||||
content: TimelineEvents[CallEventType];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,7 +417,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
// If candidates arrive before we've picked an opponent (which, in particular,
|
||||
// will happen if the opponent sends candidates eagerly before the user answers
|
||||
// the call) we buffer them up here so we can then add the ones from the party we pick
|
||||
private remoteCandidateBuffer = new Map<string, RTCIceCandidate[]>();
|
||||
private remoteCandidateBuffer = new Map<string, MCallCandidates["candidates"]>();
|
||||
|
||||
private remoteAssertedIdentity?: AssertedIdentity;
|
||||
private remoteSDPStreamMetadata?: SDPStreamMetadata;
|
||||
@@ -1156,7 +1167,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
this.terminate(CallParty.Local, reason, !suppressEvent);
|
||||
// We don't want to send hangup here if we didn't even get to sending an invite
|
||||
if ([CallState.Fledgling, CallState.WaitLocalMedia].includes(this.state)) return;
|
||||
const content: IContent = {};
|
||||
const content: Omit<MCallHangupReject, "version" | "call_id" | "party_id" | "conf_id"> = {};
|
||||
// Don't send UserHangup reason to older clients
|
||||
if ((this.opponentVersion && this.opponentVersion !== 0) || reason !== CallErrorCode.UserHangup) {
|
||||
content["reason"] = reason;
|
||||
@@ -1916,7 +1927,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
if (this.opponentPartyId !== null) {
|
||||
try {
|
||||
await this.sendVoipEvent(EventType.CallSelectAnswer, {
|
||||
selected_party_id: this.opponentPartyId,
|
||||
selected_party_id: this.opponentPartyId!,
|
||||
});
|
||||
} catch (err) {
|
||||
// This isn't fatal, and will just mean that if another party has raced to answer
|
||||
@@ -2012,6 +2023,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
logger.debug(`Call ${this.callId} onNegotiateReceived() create an answer`);
|
||||
|
||||
this.sendVoipEvent(EventType.CallNegotiate, {
|
||||
lifetime: CALL_TIMEOUT_MS,
|
||||
description: this.peerConn!.localDescription?.toJSON(),
|
||||
[SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(true),
|
||||
});
|
||||
@@ -2444,13 +2456,17 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private async sendVoipEvent(eventType: string, content: object): Promise<void> {
|
||||
const realContent = Object.assign({}, content, {
|
||||
private async sendVoipEvent<K extends keyof Pick<TimelineEvents, CallEventType>>(
|
||||
eventType: K,
|
||||
content: Omit<TimelineEvents[K], "version" | "call_id" | "party_id" | "conf_id">,
|
||||
): Promise<void> {
|
||||
const realContent = {
|
||||
...content,
|
||||
version: VOIP_PROTO_VERSION,
|
||||
call_id: this.callId,
|
||||
party_id: this.ourPartyId,
|
||||
conf_id: this.groupCallId,
|
||||
});
|
||||
} as TimelineEvents[K];
|
||||
|
||||
if (this.opponentDeviceId) {
|
||||
const toDeviceSeq = this.toDeviceSeq++;
|
||||
@@ -2729,7 +2745,9 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
const candidates = this.candidateSendQueue;
|
||||
this.candidateSendQueue = [];
|
||||
++this.candidateSendTries;
|
||||
const content = { candidates: candidates.map((candidate) => candidate.toJSON()) };
|
||||
const content: Pick<MCallCandidates, "candidates"> = {
|
||||
candidates: candidates.map((candidate) => candidate.toJSON()),
|
||||
};
|
||||
if (this.candidatesEnded) {
|
||||
// If there are no more candidates, signal this by adding an empty string candidate
|
||||
content.candidates.push({
|
||||
@@ -2923,7 +2941,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
|
||||
this.remoteCandidateBuffer.clear();
|
||||
}
|
||||
|
||||
private async addIceCandidates(candidates: RTCIceCandidate[]): Promise<void> {
|
||||
private async addIceCandidates(candidates: RTCIceCandidate[] | MCallCandidates["candidates"]): Promise<void> {
|
||||
for (const candidate of candidates) {
|
||||
if (
|
||||
(candidate.sdpMid === null || candidate.sdpMid === undefined) &&
|
||||
|
@@ -34,6 +34,7 @@ export interface CallReplacesTarget {
|
||||
|
||||
export interface MCallBase {
|
||||
call_id: string;
|
||||
conf_id?: string;
|
||||
version: string | number;
|
||||
party_id?: string;
|
||||
sender_session_id?: string;
|
||||
@@ -82,7 +83,7 @@ export interface MCAllAssertedIdentity extends MCallBase {
|
||||
}
|
||||
|
||||
export interface MCallCandidates extends MCallBase {
|
||||
candidates: RTCIceCandidate[];
|
||||
candidates: Omit<RTCIceCandidateInit, "usernameFragment">[];
|
||||
}
|
||||
|
||||
export interface MCallHangupReject extends MCallBase {
|
||||
|
Reference in New Issue
Block a user