1
0
mirror of https://gitlab.com/comunic/comunicapiv2 synced 2025-03-13 01:22:38 +00:00
comunicapiv2/src/controllers/CallsController.ts

236 lines
6.0 KiB
TypeScript

import { RequestHandler } from "../entities/RequestHandler";
import { UserWebSocketRequestsHandler } from "../entities/WebSocketRequestHandler";
import { ActiveClient, UserWebSocketController } from "./UserWebSocketController";
import { EventsHelper, CallSignalFromRTCRelayEvent } 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.set(convID, {
ready: false
});
// 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 => {return {
userID: f.userID,
ready: f.activeCalls.get(convID).ready
}}))
}
/**
* Handles client signal
*
* @param h Request handler
*/
public static async OnClientSignal(h: UserWebSocketRequestsHandler) {
const callID = h.postCallId("callID");
const peerID = h.postCallPeerID(callID, "peerID"); // The ID of the user we stream the audio / video from
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()
}
/**
* Handles proxy signals
*
* @param e Event information
*/
public static async OnProxySignal(e: CallSignalFromRTCRelayEvent) {
// Extract information
const callID = Number(e.callHash.split("-")[0])
const peerID = Number(e.callHash.split("-")[1])
let targetUser = Number(e.peerID)
if(targetUser == 0)
targetUser = peerID;
// Send the message
await UserWebSocketController.SendToSpecifcClients(
(c) => c.userID == targetUser && c.activeCalls.has(callID),
() => WsMessage.NoIDMessage("new_call_signal", {
callID: callID,
peerID: peerID,
data: e.data
})
)
}
/**
* Mark a user as ready to share its streams
*
* @param h Request handler
*/
public static async MarkUserReady(h: UserWebSocketRequestsHandler) {
const callID = h.postCallId("callID");
h.wsClient.activeCalls.get(callID).ready = true;
// Notify all other users
await UserWebSocketController.SendToSpecifcClients(
(c) => c.activeCalls.has(callID) && c.userID != h.getUserId(),
() => WsMessage.NoIDMessage("user_ready", {
callID: callID,
userID: h.getUserId()
})
)
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[0], w.client)
});
// Listen to signal from RTC proxy
EventsHelper.Listen("rtc_relay_signal", async msg => {
await CallsController.OnProxySignal(msg)
})
// 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[0], client);
}
})