infobip-rtc
Version:
Infobip RTC JavaScript SDK - Infobip WebRTC API Implementation
249 lines • 12.4 kB
JavaScript
import events from "events";
import { InfobipGatewayImpl } from "./gateway/InfobipGateway";
import Status, { StatusLabels } from "./gateway/Status";
import { DefaultLogger } from "./log/impl/DefaultLogger";
import { DefaultDevice } from "./device/DefaultDevice";
import jwtDecode from "jwt-decode";
import * as log from "loglevel";
import { LoggerOptions } from "./log/LoggerOptions";
import { DefaultIncomingApplicationCall } from "./call/impl/DefaultIncomingApplicationCall";
import { DefaultOutgoingApplicationCall } from "./call/impl/DefaultOutgoingApplicationCall";
import { IncomingApplicationCallEvent } from "./call/event/IncomingApplicationCallEvent";
import { ApiEventEmitter } from "./util/ApiEventEmitter";
import { DefaultRoomCall } from "./call/impl/DefaultRoomCall";
import { FlowType } from "./call/FlowType";
import { PhoneCallOptions } from "./call/options/PhoneCallOptions";
import { DefaultOutgoingPhoneCall } from "./call/impl/DefaultOutgoingPhoneCall";
import { ApplicationErrorCode } from "./call/ApplicationErrorCode";
import { DefaultOutgoingWebrtcCall } from "./call/impl/DefaultOutgoingWebrtcCall";
import { WebrtcCallOptions } from "./call/options/WebrtcCallOptions";
import { ViberCallOptions } from "./call/options/ViberCallOptions";
import { DefaultOutgoingViberCall } from "./call/impl/DefaultOutgoingViberCall";
import { DefaultIncomingWebrtcCall } from "./call/impl/DefaultIncomingWebrtcCall";
import { IncomingWebrtcCallEvent } from "./call/event/IncomingWebrtcCallEvent";
import { RoomCallOptions } from "./call/options/RoomCallOptions";
import { Properties } from "./Properties";
import { ApplicationCallOptions } from "./call/options/ApplicationCallOptions";
import { User } from "./call/User";
import { InfobipRTCEvent } from "./event/InfobipRTCEvents";
import ValidationUtil from "./call/impl/util/ValidationUtil";
import { WsErrorLogger } from "./util/WsErrorLogger";
export class DefaultInfobipRTC {
constructor(token, rtcOptions = {}) {
this.token = token;
this.rtcOptions = rtcOptions;
if (!this.isWebRTCSupported()) {
throw new Error('Browser does not support WebRTC.');
}
rtcOptions.debug ? log.enableAll() : log.setLevel(log.levels.ERROR);
this.eventEmitter = new events.EventEmitter();
this.apiEventEmitter = new ApiEventEmitter();
this.logger = new DefaultLogger(token, new LoggerOptions(), this.rtcOptions.statsUrl, this.rtcOptions.headersProvider);
this.currentUser = this.getUserFromToken(jwtDecode(this.token));
this.gateway = new InfobipGatewayImpl(this.eventEmitter, this.logger, this.token);
this.device = new DefaultDevice(this.logger);
this.rtcConfig = rtcOptions.iceTransportPolicy === "relay" ? { iceTransportPolicy: "relay" } : {};
this.logger.setWsErrorLogger(new WsErrorLogger(this.gateway.send.bind(this.gateway)));
}
connect() {
this.validateStatus(Status.OFFLINE, 'connect');
this.configureEventHandlers.call(this);
this.gateway.connect(false);
}
disconnect() {
if (this.logger) {
this.logger.stop();
}
this.gateway.disconnect();
}
connectedUser() {
return this.currentUser;
}
on(name, handler) {
if (!Object.values(InfobipRTCEvent)
.find(apiEvent => apiEvent === name)) {
throw new Error(`Unknown event: ${name}!`);
}
this.apiEventEmitter.on(name, handler);
}
callApplication(callsConfigurationId, options) {
this.validateCallsConfigurationId(callsConfigurationId);
this.validateStatus(Status.CONNECTED, 'callApplication');
this.checkIfAnythingIsActive();
if (options === null || options === void 0 ? void 0 : options.customData) {
ValidationUtil.validateCustomData(options.customData);
}
options !== null && options !== void 0 ? options : (options = ApplicationCallOptions.builder().build());
let call = new DefaultOutgoingApplicationCall(this.eventEmitter, this.gateway, this.logger, this.rtcConfig, this.device, callsConfigurationId, options, this.currentUser.identity, this.token, this.apiUrl);
this.callActive = true;
return call;
}
callWebrtc(identity, options) {
if (!DefaultInfobipRTC.IDENTITY_PATTERN.test(identity)) {
throw Error("Invalid destination.");
}
options !== null && options !== void 0 ? options : (options = WebrtcCallOptions.builder().build());
return new DefaultOutgoingWebrtcCall(this, options, this.currentUser.identity, identity);
}
callPhone(phoneNumber, phoneCallOptions) {
if (!DefaultInfobipRTC.PHONE_NUMBER_PATTERN.test(phoneNumber)) {
throw new Error("Invalid destination");
}
phoneCallOptions !== null && phoneCallOptions !== void 0 ? phoneCallOptions : (phoneCallOptions = PhoneCallOptions.builder().build());
return new DefaultOutgoingPhoneCall(this, phoneCallOptions, this.currentUser.identity, phoneNumber);
}
callViber(phoneNumber, from, viberCallOptions) {
if (!DefaultInfobipRTC.PHONE_NUMBER_PATTERN.test(phoneNumber)) {
throw new Error("Invalid destination");
}
if (!DefaultInfobipRTC.PHONE_NUMBER_PATTERN.test(from)) {
throw Error("Invalid caller identifier.");
}
viberCallOptions !== null && viberCallOptions !== void 0 ? viberCallOptions : (viberCallOptions = ViberCallOptions.builder().build());
return new DefaultOutgoingViberCall(this, viberCallOptions, this.currentUser.identity, phoneNumber, from);
}
joinRoom(roomName, options) {
if (!roomName || !DefaultInfobipRTC.CONFERENCE_ID_PATTERN.test(roomName)) {
throw Error("Invalid room name.");
}
options !== null && options !== void 0 ? options : (options = RoomCallOptions.builder().build());
return new DefaultRoomCall(this, options, roomName);
}
debug(debug) {
if (debug) {
log.enableAll();
}
else {
log.setLevel(log.levels.ERROR);
}
}
getAudioInputDevices() {
return this.device.getAudioInputDevices();
}
setAudioInputDevice(deviceId) {
this.device.setAudioInputDevice(deviceId);
}
unsetAudioInputDevice(deviceId) {
this.device.unsetAudioInputDevice(deviceId);
}
getAudioOutputDevices() {
return this.device.getAudioOutputDevices();
}
getVideoInputDevices() {
return this.device.getVideoInputDevices();
}
setVideoInputDevice(deviceId) {
this.device.setVideoInputDevice(deviceId);
}
unsetVideoInputDevice(deviceId) {
this.device.unsetVideoInputDevice(deviceId);
}
validateCallsConfigurationId(callsConfigurationId) {
if (!callsConfigurationId || typeof (callsConfigurationId) !== 'string' || callsConfigurationId.trim().length === 0) {
throw new Error(`Invalid callsConfigurationId '${callsConfigurationId}'`);
}
}
validateStatus(requiredStatus, methodName) {
const currentStatus = this.gateway.status;
if (requiredStatus !== currentStatus) {
throw new Error(`Cannot ${methodName}, required status is ${StatusLabels.get(requiredStatus)}, current status is ${StatusLabels.get(currentStatus)}.`);
}
}
configureEventHandlers() {
this.eventEmitter.once('registered', this.handleRegistered.bind(this));
this.eventEmitter.on('registration_failed', this.handleDisconnect.bind(this));
this.eventEmitter.once('disconnected', this.handleDisconnect.bind(this));
this.eventEmitter.on('reconnecting', this.handleReconnecting.bind(this));
this.eventEmitter.on('reconnected', this.handleReconnected.bind(this));
this.eventEmitter.on('application_call', this.handleIncomingApplicationCall.bind(this));
this.eventEmitter.on('call_finished', this.handleHangup.bind(this));
}
handleRegistered(event) {
var _a;
if (event.iceServers) {
this.rtcConfig.iceServers = event.iceServers;
}
let statsUrl = this.rtcOptions.statsUrl || event.apiUrl;
this.apiUrl = event.apiUrl;
this.logger.setOptions((_a = event.loggerOptions) !== null && _a !== void 0 ? _a : new LoggerOptions());
this.logger.setLogLevel(event.logLevel);
this.logger.setApiUrl(statsUrl);
this.apiEventEmitter.emit(InfobipRTCEvent.CONNECTED, { identity: event.identity });
}
handleIncomingApplicationCall(event) {
var _a;
if (this.callActive) {
return this.apiEventEmitter.emit('call-error', { status: ApplicationErrorCode.BUSY });
}
let destination = this.currentUser.identity;
this.callActive = true;
let callsConfigurationId = event.callsConfigurationId || event.applicationId;
if (callsConfigurationId === Properties.WEBRTC_CALLS_CONFIGURATION_ID) {
if (((_a = event.internalCustomData) === null || _a === void 0 ? void 0 : _a.type) === FlowType.WEBRTC.toString()) {
let incomingCall = new DefaultIncomingWebrtcCall(this.eventEmitter, this.gateway, this.logger, this.rtcConfig, this.device, this.hasRemoteVideo(event.internalCustomData), destination, event.caller.identity, event.description, this.token, this.apiUrl, event.callId);
this.apiEventEmitter.emit(InfobipRTCEvent.INCOMING_WEBRTC_CALL, new IncomingWebrtcCallEvent(incomingCall, event.customData));
}
else {
this.logger.warn(`Received incoming basic call without matching flow type: ${JSON.stringify(event)}`);
}
}
else {
let incomingCall = new DefaultIncomingApplicationCall(this.eventEmitter, this.gateway, this.logger, this.rtcConfig, this.device, event.caller, callsConfigurationId, event.description, destination, this.token, this.apiUrl, event.callId);
this.apiEventEmitter.emit(InfobipRTCEvent.INCOMING_APPLICATION_CALL, new IncomingApplicationCallEvent(incomingCall, event.customData));
}
}
hasRemoteVideo(customData) {
return "true" === customData.isVideo;
}
handleHangup(event) {
this.callActive = false;
}
handleReconnecting(event) {
this.apiEventEmitter.emit(InfobipRTCEvent.RECONNECTING, {});
}
handleReconnected(event) {
this.apiEventEmitter.emit(InfobipRTCEvent.RECONNECTED, {});
}
handleConnectionClosed(event) {
if (this.callActive) {
let hangupStatus;
if ((event === null || event === void 0 ? void 0 : event.code) === 4000) {
hangupStatus = ApplicationErrorCode.NORMAL_HANGUP;
}
else {
hangupStatus = ApplicationErrorCode.NETWORK_ERROR;
}
this.eventEmitter.emit('hangup', { status: hangupStatus });
}
}
handleDisconnect(event) {
this.handleConnectionClosed(event);
this.apiEventEmitter.emit(InfobipRTCEvent.DISCONNECTED, { reason: event.reason });
this.eventEmitter.removeAllListeners('registered');
this.eventEmitter.removeAllListeners('registration_failed');
this.eventEmitter.removeAllListeners('disconnected');
this.eventEmitter.removeAllListeners('reconnecting');
this.eventEmitter.removeAllListeners('reconnected');
this.eventEmitter.removeAllListeners('application_call');
this.eventEmitter.removeAllListeners('hangup');
this.eventEmitter.removeAllListeners('call_finished');
}
isWebRTCSupported() {
return RTCPeerConnection && (navigator.getUserMedia ||
(navigator.mediaDevices && navigator.mediaDevices.getUserMedia));
}
getUserFromToken(token) {
let identity = token.identity;
let displayName = token.name;
return new User(identity, displayName);
}
checkIfAnythingIsActive() {
if (this.callActive) {
throw Error('Call already in progress.');
}
}
}
DefaultInfobipRTC.IDENTITY_PATTERN = /^[\p{L}\p{N}\w.\-_+=/]{3,64}$/u;
DefaultInfobipRTC.CONFERENCE_ID_PATTERN = /^[\p{L}\p{N}\w.\-_+=/]{3,250}$/u;
DefaultInfobipRTC.PHONE_NUMBER_PATTERN = /^[+]?\p{N}+$/u;
//# sourceMappingURL=DefaultInfobipRTC.js.map