You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-23 17:02:25 +03:00
@@ -15,8 +15,8 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { type MatrixEvent } from "../../../src";
|
||||
import { CallMembership, DEFAULT_EXPIRE_DURATION, type RtcMembershipData } from "../../../src/matrixrtc/CallMembership";
|
||||
import { membershipTemplate } from "./mocks";
|
||||
import { CallMembership, DEFAULT_EXPIRE_DURATION } from "../../../src/matrixrtc/CallMembership";
|
||||
import { rtcMembershipTemplate, sessionMembershipTemplate } from "./mocks";
|
||||
|
||||
function makeMockEvent(originTs = 0, content = {}): MatrixEvent {
|
||||
return {
|
||||
@@ -29,6 +29,7 @@ function makeMockEvent(originTs = 0, content = {}): MatrixEvent {
|
||||
|
||||
describe("CallMembership", () => {
|
||||
describe("SessionMembershipData", () => {
|
||||
const membershipTemplate = sessionMembershipTemplate;
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
@@ -185,15 +186,7 @@ describe("CallMembership", () => {
|
||||
});
|
||||
|
||||
describe("RtcMembershipData", () => {
|
||||
const membershipTemplate: RtcMembershipData = {
|
||||
"slot_id": "m.call#",
|
||||
"application": { "type": "m.call", "m.call.id": "", "m.call.intent": "voice" },
|
||||
"member": { user_id: "@alice:example.org", device_id: "AAAAAAA", id: "xyzHASHxyz" },
|
||||
"rtc_transports": [{ type: "livekit" }],
|
||||
"m.call.intent": "voice",
|
||||
"versions": [],
|
||||
};
|
||||
|
||||
const membershipTemplate = rtcMembershipTemplate;
|
||||
it("rejects membership with no slot_id", () => {
|
||||
expect(() => {
|
||||
new CallMembership(makeMockEvent(0, { ...membershipTemplate, slot_id: undefined }));
|
||||
|
||||
@@ -14,12 +14,29 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { encodeBase64, EventType, MatrixClient, type MatrixError, type MatrixEvent, type Room } from "../../../src";
|
||||
import {
|
||||
encodeBase64,
|
||||
EventType,
|
||||
MatrixClient,
|
||||
MatrixEvent,
|
||||
RelationType,
|
||||
type MatrixError,
|
||||
type Room,
|
||||
} from "../../../src";
|
||||
import { KnownMembership } from "../../../src/@types/membership";
|
||||
import { MatrixRTCSession, MatrixRTCSessionEvent } from "../../../src/matrixrtc/MatrixRTCSession";
|
||||
import { Status, type EncryptionKeysEventContent } from "../../../src/matrixrtc/types";
|
||||
import { secureRandomString } from "../../../src/randomstring";
|
||||
import { makeMockEvent, makeMockRoom, membershipTemplate, makeKey, type MembershipData, mockRoomState } from "./mocks";
|
||||
import {
|
||||
makeMockEvent,
|
||||
makeMockRoom,
|
||||
sessionMembershipTemplate as membershipTemplate,
|
||||
makeKey,
|
||||
type MembershipData,
|
||||
mockRoomState,
|
||||
rtcMembershipTemplate,
|
||||
sessionMembershipTemplate,
|
||||
} from "./mocks";
|
||||
import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManager.ts";
|
||||
|
||||
const mockFocus = { type: "mock" };
|
||||
@@ -50,6 +67,7 @@ describe("MatrixRTCSession", () => {
|
||||
});
|
||||
|
||||
describe("roomSessionForRoom", () => {
|
||||
const membershipTemplate = sessionMembershipTemplate;
|
||||
it("creates a room-scoped session from room state", async () => {
|
||||
const mockRoom = makeMockRoom([membershipTemplate]);
|
||||
|
||||
@@ -206,6 +224,41 @@ describe("MatrixRTCSession", () => {
|
||||
sess = await MatrixRTCSession.sessionForRoom(client, mockRoom, callSession);
|
||||
expect(sess.memberships).toHaveLength(0);
|
||||
});
|
||||
it("fetches related events if needed from room", async () => {
|
||||
const testMembership = {
|
||||
...rtcMembershipTemplate,
|
||||
"m.relates_to": { event_id: "id", rel_type: RelationType.Reference as const },
|
||||
// hack for simple makeMockRoom construction
|
||||
"user_id": rtcMembershipTemplate.member.user_id,
|
||||
};
|
||||
|
||||
const mockRoom = makeMockRoom([testMembership]);
|
||||
mockRoom.findEventById = jest
|
||||
.fn()
|
||||
.mockImplementation((id) =>
|
||||
id === "id"
|
||||
? new MatrixEvent({ content: { ...rtcMembershipTemplate }, origin_server_ts: 100 })
|
||||
: undefined,
|
||||
);
|
||||
sess = await MatrixRTCSession.sessionForSlot(client, mockRoom, callSession);
|
||||
expect(sess.memberships[0].createdTs()).toBe(100);
|
||||
});
|
||||
it("fetches related events if needed from cs api", async () => {
|
||||
const testMembership = {
|
||||
...rtcMembershipTemplate,
|
||||
"m.relates_to": { event_id: "id", rel_type: RelationType.Reference as const },
|
||||
// hack for simple makeMockRoom construction
|
||||
"user_id": rtcMembershipTemplate.member.user_id,
|
||||
};
|
||||
|
||||
const mockRoom = makeMockRoom([testMembership]);
|
||||
mockRoom.findEventById = jest.fn().mockReturnValue(undefined);
|
||||
client.fetchRoomEvent = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ content: { ...rtcMembershipTemplate }, origin_server_ts: 100 });
|
||||
sess = await MatrixRTCSession.sessionForSlot(client, mockRoom, callSession);
|
||||
expect(sess.memberships[0].createdTs()).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOldestMembership", () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
import { ClientEvent, EventTimeline, MatrixClient } from "../../../src";
|
||||
import { RoomStateEvent } from "../../../src/models/room-state";
|
||||
import { MatrixRTCSessionManager, MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager";
|
||||
import { makeMockRoom, membershipTemplate, mockRoomState } from "./mocks";
|
||||
import { makeMockRoom, sessionMembershipTemplate, mockRoomState } from "./mocks";
|
||||
import { logger } from "../../../src/logger";
|
||||
|
||||
describe("MatrixRTCSessionManager", () => {
|
||||
@@ -42,7 +42,7 @@ describe("MatrixRTCSessionManager", () => {
|
||||
});
|
||||
|
||||
try {
|
||||
const room1 = makeMockRoom([membershipTemplate]);
|
||||
const room1 = makeMockRoom([sessionMembershipTemplate]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
@@ -58,7 +58,7 @@ describe("MatrixRTCSessionManager", () => {
|
||||
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
||||
|
||||
try {
|
||||
const room1 = makeMockRoom([{ ...membershipTemplate, application: "m.other" }]);
|
||||
const room1 = makeMockRoom([{ ...sessionMembershipTemplate, application: "m.other" }]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
@@ -76,14 +76,14 @@ describe("MatrixRTCSessionManager", () => {
|
||||
client.matrixRTC.once(MatrixRTCSessionManagerEvents.SessionEnded, rEnd);
|
||||
client.matrixRTC.once(MatrixRTCSessionManagerEvents.SessionStarted, rStart);
|
||||
|
||||
const room1 = makeMockRoom([membershipTemplate]);
|
||||
const room1 = makeMockRoom([sessionMembershipTemplate]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
await startPromise;
|
||||
|
||||
mockRoomState(room1, [{ user_id: membershipTemplate.user_id }]);
|
||||
mockRoomState(room1, [{ user_id: sessionMembershipTemplate.user_id }]);
|
||||
const roomState = room1.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
const membEvent = roomState.getStateEvents("org.matrix.msc3401.call.member")[0];
|
||||
client.emit(RoomStateEvent.Events, membEvent, roomState, null);
|
||||
@@ -112,14 +112,14 @@ describe("MatrixRTCSessionManager", () => {
|
||||
});
|
||||
|
||||
try {
|
||||
const room1 = makeMockRoom([{ ...membershipTemplate, application: "m.other" }]);
|
||||
const room1 = makeMockRoom([{ ...sessionMembershipTemplate, application: "m.other" }]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
expect(onStarted).not.toHaveBeenCalled();
|
||||
onStarted.mockClear();
|
||||
|
||||
const room2 = makeMockRoom([{ ...membershipTemplate, application: "m.notCall", call_id: "test" }]);
|
||||
const room2 = makeMockRoom([{ ...sessionMembershipTemplate, application: "m.notCall", call_id: "test" }]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1, room2]);
|
||||
|
||||
client.emit(ClientEvent.Room, room2);
|
||||
@@ -127,7 +127,7 @@ describe("MatrixRTCSessionManager", () => {
|
||||
expect(onStarted).toHaveBeenCalled();
|
||||
onStarted.mockClear();
|
||||
|
||||
mockRoomState(room2, [{ user_id: membershipTemplate.user_id }]);
|
||||
mockRoomState(room2, [{ user_id: sessionMembershipTemplate.user_id }]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room2);
|
||||
|
||||
const roomState = room2.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
@@ -137,7 +137,7 @@ describe("MatrixRTCSessionManager", () => {
|
||||
expect(onEnded).toHaveBeenCalled();
|
||||
onEnded.mockClear();
|
||||
|
||||
mockRoomState(room1, [{ user_id: membershipTemplate.user_id }]);
|
||||
mockRoomState(room1, [{ user_id: sessionMembershipTemplate.user_id }]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
||||
|
||||
const roomStateOther = room1.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
@@ -153,13 +153,13 @@ describe("MatrixRTCSessionManager", () => {
|
||||
it("Doesn't fire event if unrelated sessions ends", () => {
|
||||
const onEnded = jest.fn();
|
||||
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
|
||||
const room1 = makeMockRoom([{ ...membershipTemplate, application: "m.other_app" }]);
|
||||
const room1 = makeMockRoom([{ ...sessionMembershipTemplate, application: "m.other_app" }]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
|
||||
mockRoomState(room1, [{ user_id: membershipTemplate.user_id }]);
|
||||
mockRoomState(room1, [{ user_id: sessionMembershipTemplate.user_id }]);
|
||||
|
||||
const roomState = room1.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
const membEvent = roomState.getStateEvents("org.matrix.msc3401.call.member")[0];
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
type SessionMembershipData,
|
||||
type LivekitFocusSelection,
|
||||
} from "../../../src/matrixrtc";
|
||||
import { makeMockClient, makeMockRoom, membershipTemplate, mockCallMembership, type MockClient } from "./mocks";
|
||||
import { makeMockClient, makeMockRoom, sessionMembershipTemplate, mockCallMembership, type MockClient } from "./mocks";
|
||||
import { MembershipManager } from "../../../src/matrixrtc/MembershipManager.ts";
|
||||
import { waitFor } from "../../test-utils/test-utils.ts";
|
||||
|
||||
@@ -90,7 +90,7 @@ describe("MembershipManager", () => {
|
||||
// Default to fake timers.
|
||||
jest.useFakeTimers();
|
||||
client = makeMockClient("@alice:example.org", "AAAAAAA");
|
||||
room = makeMockRoom([membershipTemplate]);
|
||||
room = makeMockRoom([sessionMembershipTemplate]);
|
||||
// Provide a default mock that is like the default "non error" server behaviour.
|
||||
(client._unstable_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
|
||||
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||
@@ -385,7 +385,7 @@ describe("MembershipManager", () => {
|
||||
const { resolve } = createAsyncHandle(client._unstable_sendDelayedStateEvent);
|
||||
await jest.advanceTimersByTimeAsync(RESTART_DELAY);
|
||||
// first simulate the sync, then resolve sending the delayed event.
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
|
||||
resolve({ delay_id: "id" });
|
||||
// Let the scheduler run one iteration so that the new join gets sent
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
@@ -468,7 +468,7 @@ describe("MembershipManager", () => {
|
||||
describe("onRTCSessionMemberUpdate()", () => {
|
||||
it("does nothing if not joined", async () => {
|
||||
const manager = new MembershipManager({}, room, client, callSession);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
|
||||
await jest.advanceTimersToNextTimerAsync();
|
||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
|
||||
@@ -485,7 +485,7 @@ describe("MembershipManager", () => {
|
||||
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
||||
|
||||
await manager.onRTCSessionMemberUpdate([
|
||||
mockCallMembership(membershipTemplate, room.roomId),
|
||||
mockCallMembership(sessionMembershipTemplate, room.roomId),
|
||||
mockCallMembership(
|
||||
{ ...(myMembership as SessionMembershipData), user_id: client.getUserId()! },
|
||||
room.roomId,
|
||||
@@ -508,7 +508,7 @@ describe("MembershipManager", () => {
|
||||
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
||||
|
||||
// Our own membership is removed:
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
|
||||
await jest.advanceTimersByTimeAsync(1);
|
||||
expect(client.sendStateEvent).toHaveBeenCalled();
|
||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
|
||||
@@ -531,7 +531,7 @@ describe("MembershipManager", () => {
|
||||
|
||||
const { resolve } = createAsyncHandle(client._unstable_sendDelayedStateEvent);
|
||||
await jest.advanceTimersByTimeAsync(10_000);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
|
||||
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
|
||||
resolve({ delay_id: "id" });
|
||||
await jest.advanceTimersByTimeAsync(10_000);
|
||||
|
||||
@@ -900,7 +900,10 @@ describe("MembershipManager", () => {
|
||||
const manager = new MembershipManager({}, room, client, callSession);
|
||||
manager.join([]);
|
||||
expect(manager.isActivated()).toEqual(true);
|
||||
const membership = mockCallMembership({ ...membershipTemplate, user_id: client.getUserId()! }, room.roomId);
|
||||
const membership = mockCallMembership(
|
||||
{ ...sessionMembershipTemplate, user_id: client.getUserId()! },
|
||||
room.roomId,
|
||||
);
|
||||
await manager.onRTCSessionMemberUpdate([membership]);
|
||||
await manager.updateCallIntent("video");
|
||||
expect(client.sendStateEvent).toHaveBeenCalledTimes(2);
|
||||
@@ -914,7 +917,7 @@ describe("MembershipManager", () => {
|
||||
manager.join([]);
|
||||
expect(manager.isActivated()).toEqual(true);
|
||||
const membership = mockCallMembership(
|
||||
{ ...membershipTemplate, "user_id": client.getUserId()!, "m.call.intent": "video" },
|
||||
{ ...sessionMembershipTemplate, "user_id": client.getUserId()!, "m.call.intent": "video" },
|
||||
room.roomId,
|
||||
);
|
||||
await manager.onRTCSessionMemberUpdate([membership]);
|
||||
@@ -927,7 +930,7 @@ describe("MembershipManager", () => {
|
||||
it("Should prefix log with MembershipManager used", async () => {
|
||||
const spy = jest.spyOn(console, "error");
|
||||
const client = makeMockClient("@alice:example.org", "AAAAAAA");
|
||||
const room = makeMockRoom([membershipTemplate]);
|
||||
const room = makeMockRoom([sessionMembershipTemplate]);
|
||||
|
||||
const membershipManager = new MembershipManager(undefined, room, client, callSession);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManage
|
||||
import { type CallMembership, type Statistics } from "../../../src/matrixrtc";
|
||||
import { type ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTransport.ts";
|
||||
import { KeyTransportEvents, type KeyTransportEventsHandlerMap } from "../../../src/matrixrtc/IKeyTransport.ts";
|
||||
import { membershipTemplate, mockCallMembership } from "./mocks.ts";
|
||||
import { sessionMembershipTemplate, mockCallMembership } from "./mocks.ts";
|
||||
import { decodeBase64, TypedEventEmitter } from "../../../src";
|
||||
import { RoomAndToDeviceTransport } from "../../../src/matrixrtc/RoomAndToDeviceKeyTransport.ts";
|
||||
import { type RoomKeyTransport } from "../../../src/matrixrtc/RoomKeyTransport.ts";
|
||||
@@ -864,7 +864,7 @@ describe("RTCEncryptionManager", () => {
|
||||
|
||||
function aCallMembership(userId: string, deviceId: string, ts: number = 1000): CallMembership {
|
||||
return mockCallMembership(
|
||||
{ ...membershipTemplate, user_id: userId, device_id: deviceId, created_ts: ts },
|
||||
{ ...sessionMembershipTemplate, user_id: userId, device_id: deviceId, created_ts: ts },
|
||||
"!room:id",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { makeMockEvent, makeMockRoom, membershipTemplate, makeKey } from "./mocks";
|
||||
import { makeMockEvent, makeMockRoom, sessionMembershipTemplate, makeKey } from "./mocks";
|
||||
import { RoomKeyTransport } from "../../../src/matrixrtc/RoomKeyTransport";
|
||||
import { KeyTransportEvents } from "../../../src/matrixrtc/IKeyTransport";
|
||||
import { EventType, MatrixClient, RoomEvent } from "../../../src";
|
||||
@@ -48,7 +48,7 @@ describe("RoomKeyTransport", () => {
|
||||
roomEventEncryptionKeysReceivedTotalAge: 0,
|
||||
},
|
||||
};
|
||||
room = makeMockRoom([membershipTemplate]);
|
||||
room = makeMockRoom([sessionMembershipTemplate]);
|
||||
client = new MatrixClient({ baseUrl: "base_url" });
|
||||
await client.matrixRTC.start();
|
||||
transport = new RoomKeyTransport(room, client, statistics, {
|
||||
|
||||
@@ -16,13 +16,17 @@ limitations under the License.
|
||||
|
||||
import { EventEmitter } from "stream";
|
||||
|
||||
import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent } from "../../../src";
|
||||
import { CallMembership, type SessionMembershipData } from "../../../src/matrixrtc/CallMembership";
|
||||
import { EventType, MatrixEvent, type Room, RoomEvent, type MatrixClient } from "../../../src";
|
||||
import {
|
||||
CallMembership,
|
||||
type RtcMembershipData,
|
||||
type SessionMembershipData,
|
||||
} from "../../../src/matrixrtc/CallMembership";
|
||||
import { secureRandomString } from "../../../src/randomstring";
|
||||
|
||||
export type MembershipData = (SessionMembershipData | {}) & { user_id: string };
|
||||
export type MembershipData = (SessionMembershipData | RtcMembershipData | {}) & { user_id: string };
|
||||
|
||||
export const membershipTemplate: SessionMembershipData & { user_id: string } = {
|
||||
export const sessionMembershipTemplate: SessionMembershipData & { user_id: string } = {
|
||||
"application": "m.call",
|
||||
"call_id": "",
|
||||
"user_id": "@mock:user.example",
|
||||
@@ -44,6 +48,15 @@ export const membershipTemplate: SessionMembershipData & { user_id: string } = {
|
||||
"m.call.intent": "voice",
|
||||
};
|
||||
|
||||
export const rtcMembershipTemplate: RtcMembershipData = {
|
||||
"slot_id": "m.call#",
|
||||
"application": { "type": "m.call", "m.call.id": "", "m.call.intent": "voice" },
|
||||
"member": { user_id: "@alice:example.org", device_id: "AAAAAAA", id: "xyzHASHxyz" },
|
||||
"rtc_transports": [{ type: "livekit" }],
|
||||
"m.call.intent": "voice",
|
||||
"versions": [],
|
||||
};
|
||||
|
||||
export type MockClient = Pick<
|
||||
MatrixClient,
|
||||
| "getUserId"
|
||||
@@ -131,15 +144,14 @@ export function makeMockEvent(
|
||||
content: any,
|
||||
timestamp?: number,
|
||||
): MatrixEvent {
|
||||
return {
|
||||
getType: jest.fn().mockReturnValue(type),
|
||||
getContent: jest.fn().mockReturnValue(content),
|
||||
getSender: jest.fn().mockReturnValue(sender),
|
||||
getTs: jest.fn().mockReturnValue(timestamp ?? Date.now()),
|
||||
getRoomId: jest.fn().mockReturnValue(roomId),
|
||||
getId: jest.fn().mockReturnValue(secureRandomString(8)),
|
||||
isDecryptionFailure: jest.fn().mockReturnValue(false),
|
||||
} as unknown as MatrixEvent;
|
||||
return new MatrixEvent({
|
||||
event_id: "mock_event_id",
|
||||
sender,
|
||||
type,
|
||||
content,
|
||||
room_id: roomId,
|
||||
origin_server_ts: timestamp ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function mockRTCEvent({ user_id: sender, ...membershipData }: MembershipData, roomId: string): MatrixEvent {
|
||||
|
||||
@@ -230,8 +230,8 @@ export class CallMembership {
|
||||
* @throws if the data does not match any known membership format.
|
||||
*/
|
||||
public constructor(
|
||||
private matrixEvent: MatrixEvent,
|
||||
private relatedEvent?: MatrixEvent,
|
||||
private readonly matrixEvent: MatrixEvent,
|
||||
private readonly relatedEvent?: MatrixEvent,
|
||||
) {
|
||||
const data = matrixEvent.getContent() as any;
|
||||
const sessionErrors: string[] = [];
|
||||
@@ -252,8 +252,8 @@ export class CallMembership {
|
||||
const eventId = matrixEvent.getId();
|
||||
const sender = matrixEvent.getSender();
|
||||
|
||||
if (eventId === undefined) throw new Error("parentEvent is missing eventId field");
|
||||
if (sender === undefined) throw new Error("parentEvent is missing sender field");
|
||||
if (eventId === undefined) throw new Error("CallMembership matrixEvent is missing eventId field");
|
||||
if (sender === undefined) throw new Error("CallMembership matrixEvent is missing sender field");
|
||||
this.matrixEventData = { eventId, sender };
|
||||
}
|
||||
|
||||
|
||||
@@ -349,37 +349,40 @@ export class MatrixRTCSession extends TypedEventEmitter<
|
||||
const callMemberEvents = roomState.getStateEvents(EventType.GroupCallMemberPrefix);
|
||||
const callMemberships: CallMembership[] = [];
|
||||
|
||||
const createMembership = async (memberEvent: MatrixEvent): Promise<CallMembership | undefined> => {
|
||||
const relatedEventId = memberEvent.relationEventId;
|
||||
const fetchRelatedEvent = async (): Promise<MatrixEvent | undefined> => {
|
||||
const eventData = await client
|
||||
.fetchRoomEvent(room.roomId, relatedEventId!)
|
||||
.catch((e) => logger.error(`Could not get related event ${relatedEventId} for call membership`, e));
|
||||
|
||||
return eventData ? new MatrixEvent(eventData) : undefined;
|
||||
};
|
||||
const relatedEvent = relatedEventId
|
||||
? (room.findEventById(relatedEventId) ?? (await fetchRelatedEvent()))
|
||||
: undefined;
|
||||
|
||||
let membership = undefined;
|
||||
try {
|
||||
membership = new CallMembership(memberEvent, relatedEvent);
|
||||
} catch (e) {
|
||||
logger.warn("Couldn't construct call membership: ", e);
|
||||
return undefined;
|
||||
}
|
||||
// static check for newly created memberships
|
||||
if (!deepCompare(membership.slotDescription, slotDescription)) {
|
||||
logger.info(
|
||||
`Ignoring membership of user ${membership.sender} for a different session: ${JSON.stringify(membership.slotDescription)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
return membership;
|
||||
};
|
||||
|
||||
for (const memberEvent of callMemberEvents) {
|
||||
let membership = existingMemberships?.find((m) => m.eventId === memberEvent.getId());
|
||||
if (!membership) {
|
||||
const relatedEventId = memberEvent.relationEventId;
|
||||
const getRelatedMatrixEvent = async (): Promise<MatrixEvent | undefined> => {
|
||||
const eventData = await client
|
||||
.fetchRoomEvent(room.roomId, relatedEventId!)
|
||||
.catch((e) =>
|
||||
logger.error(`Could not get related event ${relatedEventId} for call membership`, e),
|
||||
);
|
||||
|
||||
return eventData ? new MatrixEvent(eventData) : undefined;
|
||||
};
|
||||
const relatedEvent = relatedEventId
|
||||
? (room.findEventById(relatedEventId) ?? (await getRelatedMatrixEvent()))
|
||||
: undefined;
|
||||
|
||||
try {
|
||||
membership = new CallMembership(memberEvent, relatedEvent);
|
||||
} catch (e) {
|
||||
logger.warn("Couldn't construct call membership: ", e);
|
||||
continue;
|
||||
}
|
||||
// static check for newly created memberships
|
||||
if (!deepCompare(membership.slotDescription, slotDescription)) {
|
||||
logger.info(
|
||||
`Ignoring membership of user ${membership.sender} for a different session: ${JSON.stringify(membership.slotDescription)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!membership) membership = await createMembership(memberEvent);
|
||||
if (!membership) continue;
|
||||
|
||||
// Dynamic checks for all (including existing) memberships
|
||||
if (membership.isExpired()) {
|
||||
@@ -394,7 +397,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
|
||||
}
|
||||
|
||||
callMemberships.sort((a, b) => a.createdTs() - b.createdTs());
|
||||
if (callMemberships.length > 1) {
|
||||
if (callMemberships.length >= 1) {
|
||||
logger.debug(
|
||||
`Call memberships in room ${room.roomId}, in order: `,
|
||||
callMemberships.map((m) => [m.createdTs(), m.sender]),
|
||||
@@ -814,7 +817,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
|
||||
*
|
||||
* This function should be called when the room members or call memberships might have changed.
|
||||
*/
|
||||
private recalculateSessionMembers = async (): Promise<void> => {
|
||||
private async recalculateSessionMembers(): Promise<void> {
|
||||
const oldMemberships = this.memberships;
|
||||
const newMemberships = await MatrixRTCSession.sessionMembershipsForSlot(
|
||||
this.room,
|
||||
@@ -863,5 +866,5 @@ export class MatrixRTCSession extends TypedEventEmitter<
|
||||
void this.encryptionManager?.onMembershipsUpdate(oldMemberships);
|
||||
|
||||
this.setExpiryTimer();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user