import { RequestHandler } from "../entities/RequestHandler"; import { UserWebSocketRequestsHandler } from "../entities/WebSocketRequestHandler"; import { ActiveClient, UserWebSocketController } from "./UserWebSocketController"; import { EventsHelper } from "../helpers/EventsHelper"; import * as ws from 'ws' import { WsMessage } from "../entities/WsMessage"; import { conf } from "../helpers/ConfigHelper"; import { RTCRelayController } from "./RTCRelayController"; /** * Legacy calls controller * * @author Pierre HUBERT */ export class CallsController { /** * Legacy calls config */ public static GetLegacyConfig(h: RequestHandler) { h.send({ enabled: false }); } /** * Get call configuration * * @param h Request handler */ public static async GetConfig(h: UserWebSocketRequestsHandler) { // Check if user is member of at least one call if(h.wsClient.activeCalls.size == 0) h.error(401, "You do not belong to any call for now!") h.send({ iceServers: conf().rtc_relay.iceServers }) } /** * Join a call * * @param h Request handler */ public static async JoinCall(h: UserWebSocketRequestsHandler) { const convID = await h.postConversationId("convID"); // If the user was active in any other calls, remove him for(const c of UserWebSocketController.active_clients.filter((f) => f.userID == h.getUserId() && f.activeCalls.has(convID))) await this.MakeUserLeaveCall(convID, c) h.wsClient.activeCalls.add(convID); // Notify all other users await UserWebSocketController.SendToSpecifcClients( (c) => c.activeCalls.has(convID) && c.userID != h.getUserId(), () => WsMessage.NoIDMessage("user_joined_call", { callID: convID, userID: h.getUserId() }) ) h.success(); } /** * Leave a call * * @param h Request handler */ public static async LeaveCall(h: UserWebSocketRequestsHandler) { // Warning ! For some technical reasons, we do not check if the user // really belongs to the conversation, so be careful when manipulating // conversation ID here const convID = h.postInt("convID"); // Check if user is already in the conversation if(!h.wsClient.activeCalls.has(convID)) { h.success(); return; } // Make the user leave the call await this.MakeUserLeaveCall(convID, h.wsClient); h.success(); } /** * Get the list of members for a call * * The user must have joined the call to get this information * * @param h Request handler */ public static async GetMembersList(h: UserWebSocketRequestsHandler) { const convID = h.postCallId("callID"); h.send(UserWebSocketController.active_clients.filter( (f) => f.activeCalls.has(convID) ).map(f => f.userID)) } /** * Handles client signal * * @param h Request handler */ public static async OnClientSignal(h: UserWebSocketRequestsHandler) { const callID = h.postCallId("callID"); const peerID = h.postCallPeerID(callID, "peerID"); const type = h.postString("type"); const data = h.postJSON("data"); if(type !== "SDP" && type !== "CANDIDATE") h.error(401, "Invalid candidate type"); if(type == "SDP" && (!data.hasOwnProperty("type") || data.type !== "offer" || !data.hasOwnProperty("sdp"))) h.error(401, "Invalid SDP signal!") if(type == "CANDIDATE" && (!data.hasOwnProperty("candidate") || !data.hasOwnProperty("sdpMLineIndex") || !data.hasOwnProperty("sdpMid"))) h.error(401, "Invalid candidate signal!") await RTCRelayController.SendMessage({ title: "signal", callHash: callID+"-"+peerID, peerId: String(peerID === h.getUserId() ? 0 : peerID), data: { type: type, data: data } }); h.success() } /** * Make the client leave the call * * @param c Client information */ public static async MakeUserLeaveCall(convID: number, c: ActiveClient) { c.activeCalls.delete(convID) // Notify user (if possible) if(c.ws.readyState == ws.OPEN) UserWebSocketController.SendToClient(c, WsMessage.NoIDMessage("call_closed",convID)); // Notify all other users await UserWebSocketController.SendToSpecifcClients( (c) => c.activeCalls.has(convID), () => WsMessage.NoIDMessage("user_left_call", { callID: convID, userID: c.userID }) ) } } // Listen for websocket closed EventsHelper.Listen("user_ws_closed", async w => { for(const convID of w.client.activeCalls) await CallsController.MakeUserLeaveCall(convID, w.client) }); // Close all call when RTC WS is closed EventsHelper.Listen("rtc_relay_ws_closed", async () => { for(const client of UserWebSocketController.active_clients) { for(const convID of client.activeCalls) await CallsController.MakeUserLeaveCall(convID, client); } })