UNPKG

@expertflow/sdk-for-customer-facing-channels

Version:

NPM Package to install SDK for Chat, WebRTC Audio, and Video Calls that will land on Cisco Contact Center Agents handling calls using Cisco Jabber or similar in any node-based application.

1,748 lines (1,675 loc) 191 kB
import { io, Socket } from "socket.io-client"; let socket: Socket | undefined; let wssServerIp: string; let uriServerIp: string; let diallingURI: string; let sipExtension: string; let extensionPassword: string; let enable_sip_logs: boolean; let enableLogs: boolean; let wssPort: any; let IP: string; let dialerURI: string; let sipPassword: string; let ext: any; let session: any; let mediaElement: any; let mediaLocal: any; let userAgent: any; let ex: any; var remote_stream:any; let remoteVideo:any; let register = false; let displayMediaStrea: any; let toggleVideo: any; let video: any; var audio: any; let screen: any; let mediaAcquire = "end"; let endCallBtn = false; var mySessionDescriptionHandlerFactory = null; let dialedNumber: any; let callVariableArray: any = []; // Initialize an object to keep track of function locks const functionLocks: any = {}; let canCallFunction: boolean = true; let callEndDialogId: any; let endCall: boolean = false; let calls: any = []; let consultSession: any; let registerer: any; let againRegister: boolean = false; let sessionall: any = null; let remoteSession: any = null; let loginid: any = null; let wrapUpEnabler: any = null; let agentInfo: boolean = false; let callbackFunction: any = null; let remoteStream: any; let localStream: any; let dialogStatedata: any = null; let invitedata: any = null; let outBoundDialingData: any = null; let consultCalldata: any = null; let sipConfigs: any = {}; let isConversationActive: boolean = false; declare var sip_id: any, data: any; const dialogStatedata1: any = { event: "dialogState", response: { loginId: null, dialog: { id: null, fromAddress: null, dialedNumber: null, customerNumber: null, serviceIdentifer: null, dnis: null, callType: null, ani: null, wrapUpReason: null, callEndReason: null, queueName: null, queueType: null, associatedDialogUri: null, secondaryId: null, participants: [ { actions: { action: ["TRANSFER_SST", "HOLD", "SEND_DTMF", "DROP"], }, mediaAddress: null, mediaAddressType: "SIP.js/0.21.2-CTI/Expertflow", startTime: null, state: null, stateCause: null, stateChangeTime: null, mute: false, }, ], callVariables: { CallVariable: [], }, state: null, isCallAlreadyActive: false, callbackNumber: null, outboundClassification: null, scheduledCallbackInfo: null, isCallEnded: 0, eventType: "PUT", mediaType: null, callOriginator: "webrtc", }, }, }; const outBoundDialingData12: any = { event: "outboundDialing", response: { loginid: null, dialog: { id: null, ani: null, customerNumber: null, associatedDialogUri: null, callbackNumber: null, outboundClassification: null, scheduledCallbackInfo: null, isCallEnded: 0, eventType: "PUT", callType: null, queueName: null, queueType: null, dialedNumber: null, dnis: null, secondaryId: null, state: "INITIATING", isCallAlreadyActive: false, wrapUpReason: null, wrapUpItems: null, callEndReason: null, fromAddress: null, callVariables: { CallVariable: [], }, participants: [ { actions: { action: ["TRANSFER_SST", "HOLD", "SEND_DTMF", "DROP"], }, mediaAddress: null, mediaAddressType: "SIP.js/0.21.2-CTI/Expertflow", startTime: null, state: null, stateCause: null, stateChangeTime: null, mute: false, }, ], mediaType: null, callOriginator: "webrtc", }, }, }; const consultCalldata1: any = { event: "ConsultCall", response: { loginid: null, dialog: { id: null, ani: null, customerNumber: null, associatedDialogUri: null, callbackNumber: null, outboundClassification: null, scheduledCallbackInfo: null, isCallEnded: 0, eventType: "PUT", callType: null, queueName: null, queueType: null, dialedNumber: null, dnis: null, serviceIdentifier: null, secondaryId: null, state: "INITIATING", isCallAlreadyActive: false, wrapUpReason: null, wrapUpItems: null, callEndReason: null, fromAddress: null, callVariables: { CallVariable: [], }, participants: [ { actions: { action: ["TRANSFER_SST", "HOLD", "SEND_DTMF", "DROP"], }, mediaAddress: null, mediaAddressType: "SIP.js/0.21.2-CTI/Expertflow", startTime: null, state: null, stateCause: null, stateChangeTime: null, mute: false, }, ], mediaType: null, callOriginator: "webrtc", }, }, }; const invitedata1: any = { event: "newInboundCall", response: { loginId: null, dialog: { id: null, ani: null, customerNumber: null, associatedDialogUri: null, callbackNumber: null, outboundClassification: null, scheduledCallbackInfo: null, isCallEnded: 0, eventType: "PUT", callType: null, queueName: null, queueType: null, dialedNumber: null, dnis: null, serviceIdentifier: null, secondaryId: null, state: "ALERTING", isCallAlreadyActive: false, wrapUpReason: null, wrapUpItems: null, callEndReason: null, fromAddress: null, callVariables: { CallVariable: [], }, participants: [ { actions: { action: ["ANSWER"], }, mediaAddress: null, mediaAddressType: "SIP.js/0.21.2-CTI/Expertflow", startTime: null, state: null, stateCause: null, stateChangeTime: null, mute: false, }, ], mediaType: null, callOriginator: "webrtc", }, }, }; interface mediaConversion { event: string; // Always a string status: 'error' | 'success' | null; // Can be 'error', 'success', or null loginid: string; // Always a string dialog: { id: string | null; // Can be a string or null eventRequest: 'local' | 'remote' | null; // Can be 'local', 'remote', or null stream: 'video' | 'screenshare' | null; // Can be 'video', 'screenshare', or null streamStatus: 'on' | 'off' | null; // Can be 'on', 'off', or null errorReason: string | null; // Can be a string or null timeStamp: any | null; // Can be a Date object or null }; } var mediaConversion: mediaConversion={ event: "mediaConversion", status: null, // error , success loginid: "", dialog: { id: null, eventRequest: null, //local , remote stream: null, // video , screenshare streamStatus: null, //on , off errorReason: null, timeStamp: null, }, } var inviteDelegate = { onAck(ack:any) { console.log("onAck MESSAGE ******** ", ack); }, onBye(bye:any) { // need to discuss this console.log("onBye MESSAGE ******** ", bye); // event = ConsultCall, dialogState , newInboundCall if ( dialogStatedata && dialogStatedata.event && dialogStatedata.event === "ConsultCall" ) { const match = bye.incomingByeRequest.message.data.match(/text="([^"]+)"/); if (match && match[1]) { dialogStatedata.response.dialog.callEndReason = match[1]; } } if (invitedata && invitedata.event && invitedata.event === "ConsultCall") { const match = bye.incomingByeRequest.message.data.match(/text="([^"]+)"/); if (match && match[1]) { invitedata.response.dialog.callEndReason = match[1]; } } if ( dialogStatedata && dialogStatedata.event && dialogStatedata.event === "dialogState" ) { const match = bye.incomingByeRequest.message.data.match(/text="([^"]+)"/); if (match && match[1]) { dialogStatedata.response.dialog.callEndReason = match[1]; } } if (invitedata && invitedata.event && invitedata.event === "dialogState") { const match = bye.incomingByeRequest.message.data.match(/text="([^"]+)"/); if (match && match[1]) { invitedata.response.dialog.callEndReason = match[1]; } } }, }; export function include(file: string): void { const script = document.createElement("script"); script.src = file; script.type = "text/javascript"; script.defer = true; const head = document?.getElementsByTagName("head").item(0); if (head) { head.appendChild(script); } } declare var SIP: any; /** * * @returns */ const getDynamicExt = () => new Promise((resolve, reject) => { resolve(sipExtension); }); export function widgetConfigs( ccmUrl: string, widgetIdentifier: string, callback: (data: any) => void ) { fetch(`${ccmUrl}/widget-configs/${widgetIdentifier}`) .then((response) => response.json()) .then((data) => { callback(data); const webRtc = data.webRtc; if (webRtc) { wssServerIp = webRtc.wssFs ?? "defaultWssFsValue"; // Add a fallback value if undefined uriServerIp = webRtc.uriFs ?? "defaultUriFsValue"; diallingURI = webRtc.diallingUri ?? "defaultDiallingUri"; sipExtension = webRtc.sipExtension ?? "defaultSipExtension"; extensionPassword = webRtc.extensionPassword ?? "defaultPassword"; enable_sip_logs = webRtc.enabledSipLogs ?? false; enableLogs = enable_sip_logs; IP = uriServerIp; dialerURI = "sip:" + diallingURI + "@" + uriServerIp; sipPassword = extensionPassword; } else { console.error("webRtc configuration is missing in the response."); } }); } export function getPreChatForm( formUrl: string, formId: string, callback: (data: any) => void ) { fetch(`${formUrl}/forms/${formId}`) .then((response) => response.json()) .then((data) => { callback(data); }); } export function formValidation(formUrl: string, callback: (data: any) => void) { fetch(`${formUrl}/formValidation`) .then((response) => response.json()) .then((data) => { callback(data); }); } /** * Function to Establish Connection * Two Parameters * 1- Customer Data * 2- Call Function of socketEventListeners() * @param {*} serviceIdentifier * @param {*} channelCustomerIdentifier * @param {*} callback */ export function establishConnection( socket_url: string, serviceIdentifier: string, channelCustomerIdentifier: string, callback: (data: any) => void ) { try { if (socket !== undefined && socket.connected) { console.log("Resuming Existing Connection"); eventListeners((data) => { callback(data); }); } else { if (socket_url !== "") { console.log("Starting New Connection"); const origin = new URL(socket_url).origin; const path = new URL(socket_url).pathname; socket = io(origin, { path: path == "/" ? "" : path + "/socket.io", auth: { serviceIdentifier: serviceIdentifier, channelCustomerIdentifier: channelCustomerIdentifier, }, }); eventListeners((data) => { callback(data); }); } } } catch (error) { callback(error); } } /** * Socket EventListener Function * 1- Socket Connection Event * 2- Socket Discount Event * 3- Socket Connection Error Event * 4- Socket Message Arrived Event * 5- Socket End Conversation Event * 6- Socket Error * 7- Channel Session Started Event * @param {*} callback */ export function eventListeners(callback: (data: any) => void) { socket?.on("connect", () => { if (socket?.id !== undefined) { console.log(`you are connected with socket:`, socket); const error = localStorage.getItem("widget-error"); if (error) { callback({ type: "SOCKET_RECONNECTED", data: socket }); } else { callback({ type: "SOCKET_CONNECTED", data: socket }); } } }); socket?.on("CHANNEL_SESSION_STARTED", (data:any) => { console.log(`Channel Session Started Data: `, data); const gtmObject = { type: "gtmDataLayer", data: { type: "CHAT STARTED", data: { customerIdentifier: data.header.channelData.channelCustomerIdentifier, serviceIdentifier: data.header.channelData.serviceIdentifier, }, }, }; window.parent.postMessage(gtmObject, "*"); callback({ type: "CHANNEL_SESSION_STARTED", data: data }); }); socket?.on("MESSAGE_RECEIVED", (message:any) => { console.log(`MESSAGE_RECEIVED received: `, message); callback({ type: "MESSAGE_RECEIVED", data: message }); }); socket?.on("disconnect", (reason:any) => { console.error(`Connection lost with the server: `, reason); // const gtmObject = { // type: "gtmDataLayer", // data: { // type: "CHAT ENDED", // data:reason // }, // }; // window.parent.postMessage(gtmObject, "*"); callback({ type: "SOCKET_DISCONNECTED", data: reason }); }); socket?.on("connect_error", (error:any) => { console.log( `unable to establish connection with the server: `, error.message ); localStorage.setItem("widget-error", "1"); callback({ type: "CONNECT_ERROR", data: error }); }); socket?.on("CHAT_ENDED", (data:any) => { console.log(`CHAT_ENDED received: `, data); callback({ type: "CHAT_ENDED", data: data }); socket?.disconnect(); }); socket?.on("ERRORS", (data:any) => { console.error(`ERRORS received: `, data); callback({ type: "ERRORS", data: data }); }); } interface WebChannelData { browserDeviceInfo: any; queue: any; locale: any; formData: any; } interface AdditionalAttributesData { key: string; type: string; value: WebChannelData; } interface ChatRequestData { channelCustomerIdentifier: string; serviceIdentifier: string; additionalAttributes: AdditionalAttributesData[]; } export function chatRequest(data: any) { try { if (data) { const additionalAttributesData: AdditionalAttributesData[] = []; const webChannelDataObj: AdditionalAttributesData = { key: "WebChannelData", type: "WebChannelData", value: { browserDeviceInfo: data.data.browserDeviceInfo, queue: data.data.queue, locale: data.data.locale, formData: data.data.formData, }, }; additionalAttributesData.push(webChannelDataObj); const obj: ChatRequestData = { channelCustomerIdentifier: data.data.channelCustomerIdentifier, serviceIdentifier: data.data.serviceIdentifier, additionalAttributes: additionalAttributesData, }; if (socket) { socket.emit("CHAT_REQUESTED", obj); console.log(`SEND CHAT_REQUESTED DATA:`, obj); } } } catch (error) { throw error; } } export function voiceRequest(data: any) { try { if (data) { const additionalAttributesData: AdditionalAttributesData[] = []; const webChannelDataObj: AdditionalAttributesData = { key: "WebChannelData", type: "WebChannelData", value: { browserDeviceInfo: data.data.browserDeviceInfo, queue: data.data.queue, locale: data.data.locale, formData: data.data.formData, }, }; additionalAttributesData.push(webChannelDataObj); const obj: ChatRequestData = { channelCustomerIdentifier: data.data.channelCustomerIdentifier, serviceIdentifier: data.data.serviceIdentifier, additionalAttributes: additionalAttributesData, }; if (socket) { socket.emit("VOICE_REQUESTED", obj); console.log(`SEND VOICE_REQUESTED DATA:`, obj); } } } catch (error) { throw error; } } export function sendMessage(message:any, dialogId:any) { var destination = 0 var index = getCallIndex(dialogId); var sessionall = null if (index !== -1) { sessionall = calls[index]; } if (!sessionall) { return } //callType = "OUT" means its a Customer Call and we are on Customer Widget if (sessionall.response.dialog.callType == "OUT") { destination = sessionall.additionalDetail.agentExt } else { if (typeof sessionall.session.incomingInviteRequest !== 'undefined') { destination = sessionall.session.incomingInviteRequest.message.from.uri.normal.user } else if (typeof sessionall.session.outgoingInviteRequest !== 'undefined') { destination = sessionall.session.outgoingInviteRequest.message.to.uri.normal.user } } const message_targetUri_value = new SIP.URI("sip", destination, sipConfigs.uriFs) message = new SIP.Messager(userAgent, message_targetUri_value, JSON.stringify(message)); message.message(); } export function chatEnd(data: any) { // Chat Disconnection Socket Event if (socket) { socket.emit("CHAT_ENDED", data); } } /** * * @param {*} data */ export function resumeChat(data: any, callback: (res: any) => void) { if (socket) { const gtmObject = { type: "gtmDataLayer", data: { type: "BROWSER NAVIGATED", data: { customerIdentifier: data.channelCustomerIdentifier, serviceIdentifier: data.serviceIdentifier, }, }, }; console.log(data, "Resume Chat Before Emit Console.log"); socket.emit("CHAT_RESUMED", data, (res: any) => { if (res) { console.log(res, "resume chat response in sdk."); callback(res); } }); } } /** * * @param {*} data */ export function sendJoinConversation(data: any) { socket?.emit("joinConversation", data, (res: any) => { console.log("[sendJoinConversation] ", data); return res; }); } /** * File Upload to File Engine Function * @param {*} formData * @param {*} callback */ export function uploadToFileEngine( fileServerUrl: string, formData: any, callback: any ) { fetch(`${fileServerUrl}/api/uploadFileStream`, { method: "POST", body: formData, }) .then((response) => response.json()) .then((result) => { console.log("Success: ", result); callback(result); }) .catch((error) => { console.error("Error: ", error); callback(error); }); } /** * Set Conversation Data Api */ export async function setConversationData( url: string, conversationId: string, data: any ) { const response = await fetch(`${url}/${conversationId}/conversation-data`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }); return response; } /** * Set Conversation Data Api By Customer Channel Identifier */ export async function setConversationDataByCustomerIdentifier( url: string, channelIdentifier: string, data: any, callback: any ) { try { const response = await fetch( `${url}/${channelIdentifier}/conversation-data-by-identifier`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), } ); if (response.status === 403) { console.error("Forbidden: The server returned a 403 Forbidden response."); callback(response); } if (!response.ok) { console.error("Network response was not ok"); callback(response); } const result = await response.json(); console.log("Success:", result); callback(result); } catch (error) { console.error("Error:", error); callback(error); // Re-throw the error for the caller to handle } } /** * Get Conversation Data Api By Customer Identifier */ export async function getConversationDataByCustomerIdentifier( url: string, channelIdentifier: string, callback: any ) { try { const response = await fetch(`${url}/get/${channelIdentifier}`, { method: "GET", // Specify the HTTP method as GET headers: { "Content-Type": "application/json", // Set appropriate headers if needed }, }); if (response.status === 403) { console.error("Forbidden: The server returned a 403 Forbidden response."); callback(response); } else if (!response.ok) { console.error( `Failed to fetch data from ${url}/get/${channelIdentifier}: ${response.status} ${response.statusText}` ); callback(response); } else { const data = await response.json(); callback(data); } } catch (error) { console.error("Error:", error); callback(error); // Re-throw the error for the caller to handle } } /** * Get Conversation Data Api */ export async function getConversationData(url: string, conversationId: string) { const response = await fetch(`${url}/${conversationId}/conversation-data`); if (!response.ok) { throw new Error( `Failed to fetch data from ${url}/${conversationId}/conversation-data: ${response.status} ${response.statusText}` ); } const data = await response.json(); return data; } /** * Callback Request To ECM * @param {*} payload * @param {*} url */ export function callbackRequest(url: string, payload: any, callback: any) { try { // Make an API Call fetch(`${url}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }) .then((response) => response.json()) .then((data) => { // Handle the API response here console.log("API response:", data); callback(data); }) .catch((error) => { // Handle any errors that occur during the API call console.error("API Call Error", error); callback(error); }); } catch (error) { console.error("API Function Error", error); callback(error); } } /** * Webhook Notifications Functions * @param {*} data */ export function webhookNotifications(webhookUrl:string, additionalData:any, data:any) { // Constructing the message dynamically based on the keys and values in the data object let imageUrl = modifyUrlPath(additionalData.agent_url, additionalData.icon); let formattedText = ""; for (const [key, value] of Object.entries(data)) { formattedText += `${capitalizeFirstLetter(key)}: ${ value ? value : "N/A" }\n`; } let newAgentUrl = modifyUrlPath(additionalData.agent_url, "/unified-agent/"); formattedText += `To respond: <a href='${newAgentUrl}'>Click here</a>\n`; let messageObj = { cards: [ { header: { title: `${ data.first_name ? data.first_name : "Customer" } started a new chat`, imageUrl: imageUrl, imageStyle: "IMAGE", }, sections: [ { widgets: [ { textParagraph: { text: formattedText, }, }, ], }, ], }, ], }; fetch(`${webhookUrl}`, { method: "POST", body: JSON.stringify(messageObj), // Formatting as a JSON object for Google Workspace webhook headers: { "Content-Type": "application/json; charset=UTF-8", }, }) .then((response) => { if (!response.ok) { return response.json().then((err) => Promise.reject(err)); } return response.json(); }) .then((result) => { console.log("Success: ", result); }) .catch((error) => { console.error("Error: ", error); }); } function modifyUrlPath(originalUrl: string, newPath: string): string | null { try { const url = new URL(originalUrl); url.pathname = newPath; return url.toString(); } catch (error) { console.error("Invalid URL:", error); return null; } } function capitalizeFirstLetter(string:string) { return string.charAt(0).toUpperCase() + string.slice(1).replace(/_/g, " "); } /** * * @param {*} eventsCallback */ export function dialCall(eventsCallback: any) { getDynamicExt() .then((extension: any) => { ext = extension; console.log(wssServerIp, "ip at call time"); userAgent = new SIP.UA({ uri: extension + "@" + uriServerIp, transportOptions: { wsServers: wssServerIp, traceSip: true }, authorizationUser: extension, password: extensionPassword, log: { builtinEnabled: enableLogs, level: 3, }, register: true, }); userAgent.start(); if (typeof eventsCallback === "function") { let event = { event: "get_dynamic_ext", response: extension, cause: "", }; eventsCallback(event); } userAgent.on("unregistered", function (response: any, cause: any) { register = false; if (typeof eventsCallback === "function") { let event = { event: "unregistered", response: response, cause: cause, }; eventsCallback(event); } }); userAgent.on("registered", function () { register = true; if (typeof eventsCallback === "function") { let event = { event: "registered", response: "", cause: "", }; eventsCallback(event); } }); userAgent.on("registrationFailed", function (response: any, cause: any) { if (typeof eventsCallback === "function") { let event = { event: "registrationFailed", response: response, cause: cause, }; eventsCallback(event); } }); }) .catch((rej: any) => { if (typeof eventsCallback === "function") { let event = { event: "get_dynamic_ext", response: "", cause: rej, }; eventsCallback(event); } }); } /** * * @param {*} mediaType * @param {*} videoName * @param {*} videoLocal * @param {*} userData * @param {*} eventsCallback * @returns */ export const sendInvite = ( mediaType: any, videoName: any, videoLocal: any, userData: any, eventsCallback: any ) => { return new Promise((resolve, reject) => { let mediaConstraints = { audio: true, video: true }; toggleVideo = "web_cam"; mediaElement = document.getElementById(videoName); if (videoLocal === "") { mediaLocal = ""; } else { mediaLocal = document.getElementById(videoLocal); } audio = "true"; if (mediaType === "audio") { mediaConstraints = { audio: true, video: false }; video = "false"; } else { mediaConstraints = { audio: true, video: true }; video = "true"; } screen = "false"; console.log("invite function has been triggered"); if (userData !== null) { var extraHeaderString = []; var index = 0; for (const key in userData) { if (typeof userData[key] === "string") { var keyvalue = userData[key].trim(); extraHeaderString.push( "X-variable" + index + ":" + key + "|" + keyvalue ); index++; } else { console.warn( `Value for key ${key} is not a string and will be skipped.` ); } } } session = userAgent.invite("sip:" + diallingURI + "@" + uriServerIp, { sessionDescriptionHandlerOptions: { constraints: mediaConstraints, }, extraHeaders: extraHeaderString, }); if (typeof eventsCallback === "function") { let event = { event: "Channel Creating", response: "", cause: "", }; eventsCallback(event); } session.on("accepted", function () { // Assumes you have a media element on the DOM const remoteStream = new MediaStream(); if (video === "false") { console.log("closing video"); } session.sessionDescriptionHandler.peerConnection .getReceivers() .forEach((receiver: any) => { if (receiver.track) { console.log(receiver.track); remoteStream.addTrack(receiver.track); } }); mediaElement.srcObject = remoteStream; if (mediaLocal !== "") { const localStream = new MediaStream(); session.sessionDescriptionHandler.peerConnection .getSenders() .forEach((sender: any) => { if (sender.track.kind === "video") { console.log(sender.track); localStream.addTrack(sender.track); } }); mediaLocal.srcObject = localStream; } if (typeof eventsCallback === "function") { let event = { event: "session-accepted", response: "", cause: "", }; eventsCallback(event); } }); session.on("progress", function (response: any) { if (typeof eventsCallback === "function") { let event = { event: "session-progress", response: response, cause: "", }; eventsCallback(event); } }); session.on("rejected", function (response: any, cause: any) { if (typeof eventsCallback === "function") { let event = { event: "session-rejected", response: response, cause: cause, }; eventsCallback(event); } }); session.on("failed", function (response: any, cause: any) { if (typeof eventsCallback === "function") { let event = { event: "session-failed", response: response, cause: cause, }; eventsCallback(event); } var options = { all: true, }; userAgent.unregister(options); }); session.on("terminated", function (message: any, cause: any) { closeSession(eventsCallback); if (typeof eventsCallback === "function") { let event = { event: "session-terminated", response: message, cause: cause, }; eventsCallback(event); } }); session.on("bye", function (request: any) { if (typeof eventsCallback === "function") { let event = { event: "session-bye", response: request, cause: "", }; eventsCallback(event); } }); session.on("iceConnectionDisconnected", function () { if (typeof eventsCallback === "function") { let event = { event: "session-iceConnectionDisconnected", response: "request", cause: "", }; eventsCallback(event); } }); session.on("SessionDescriptionHandler-created", function () { session.sessionDescriptionHandler.on( "getDescription", function (sdpWrapper: any) { if (typeof eventsCallback === "function") { let event = { event: "session-SessionDescriptionHandler-getDescription", response: sdpWrapper, cause: "", }; eventsCallback(event); } } ); session.sessionDescriptionHandler.on("Media acquire start", function () { mediaAcquire = "start"; if (typeof eventsCallback === "function") { let event = { event: "session-SessionDescriptionHandler-Media acquire start", response: "", cause: "", }; eventsCallback(event); } }); session.sessionDescriptionHandler.on("Media acquire end", function () { if (endCallBtn === true) { terminateCurrentSession(() => { eventsCallback(); }); endCallBtn = false; } mediaAcquire = "end"; if (typeof eventsCallback === "function") { let event = { event: "session-SessionDescriptionHandler-Media acquire end", response: "", cause: "", }; eventsCallback(event); } }); if (typeof eventsCallback === "function") { let event = { event: "session-SessionDescriptionHandler-created", response: "", cause: "", }; eventsCallback(event); } }); resolve("successful"); }); }; /** * Close Video Function */ export function closeVideo() { let pc = session.sessionDescriptionHandler.peerConnection; pc.getSenders().find(function (s: any) { if (s.track.readyState == "live" && s.track.kind === "video") { s.track.stop(); } }); } /** * * @param {*} eventsCallback */ /** * * @param {*} eventsCallback */ export function terminateCurrentSession(eventsCallback: any) { promise1 .then((value) => { userAgent.stop(); }) .then(function () { return userAgent.transport.disconnect(); }) .then(function () { var options = { all: true, }; return userAgent.unregister(options); }) .then(function () { if (typeof eventsCallback === "function") { let event = { event: "session-session_ended", response: "userAgent unregistered", cause: "", }; eventsCallback(event); } }) .catch(function (error) { if (typeof eventsCallback === "function") { let event = { event: "session-termination-failed", response: "An error occurred during session termination", cause: error.message, }; eventsCallback(event); } }); } /** * Promise * @param {resolve , reject} */ const promise1 = new Promise((resolve, reject) => { resolve("Success!"); }); /** * * * @param {*} eventsCallback */ export function closeSession(eventsCallback: any) { if (mediaAcquire === "start") { endCallBtn = true; if (typeof eventsCallback === "function") { let event = { event: "session-terminated", response: "Session terminated due to media acquire start", cause: "", }; eventsCallback(event); } } else { terminateCurrentSession(eventsCallback); } } /** * Audio Call Control */ export function audioControl() { let pc = session.sessionDescriptionHandler.peerConnection; if (audio === "true") { pc.getSenders().find(function (s: any) { console.log(s.track.kind + "--------------" + s.track.readyState); if (s.track.readyState == "live" && s.track.kind === "audio") { s.track.stop(); } }); audio = "false"; } else { navigator.mediaDevices .getUserMedia({ audio: true, }) .then(function (stream) { let audioTrack = stream.getAudioTracks()[0]; var sender = pc.getSenders().find(function (s: any) { return s.track.kind == audioTrack.kind; }); console.log("found sender:", sender); sender.replaceTrack(audioTrack); }) .catch(function (err) { console.error("Error happens:", err); }); audio = "true"; } } /** * Video Call Control */ export function videoControl() { let pc = session.sessionDescriptionHandler.peerConnection; if (video === "true") { pc.getSenders().find(function (s: any) { console.log(s.track.kind + "--------------" + s.track.readyState); if (s.track.readyState == "live" && s.track.kind === "video") { s.track.stop(); } }); video = "false"; } else { navigator.mediaDevices .getUserMedia({ video: true, }) .then(function (stream) { let videoTrack = stream.getVideoTracks()[0]; var sender = pc.getSenders().find(function (s: any) { return s.track.kind == videoTrack.kind; }); console.log("found sender:", sender); sender.replaceTrack(videoTrack); mediaLocal.srcObject = stream; mediaLocal.play(); }) .catch(function (err) { console.error("Error happens:", err); }); video = "true"; } } /** * ScreenControl */ export function screenControl() { if (screen === "false") { screen = "true"; } else { } } /** * Webhook Notifications Functions * @param {*} data */ export function authenticateRequest( authenticatorUrl: string, authData: any, callback: any ) { console.log( "authenticateRequest: in sdk function:", JSON.stringify(authData) ); fetch(`${authenticatorUrl}/verifySecureLink`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(authData), }) .then(async (response) => { const contentType = response.headers.get("content-type"); if (!response.ok) { let errorMessage = "Network response was not ok"; if (response.status === 400) { // Handle the 400 Bad Request error here const errorData = await response.json(); errorMessage = "400 Bad Request"; // Custom handling for the error response callback({ error: true, message: errorMessage, data: errorData }); throw new Error(errorMessage); // Stop the promise chain } else if (response.status === 500) { errorMessage = "500 Internal Server Error"; } callback({ error: true, message: errorMessage }); throw new Error(errorMessage); // Stop the promise chain } if (contentType && contentType.includes("application/json")) { return response.json(); } else { return response.text(); // Handle plain text response } }) .then((result) => { // This will not be executed if an error was thrown in the previous block // console.log('Authentication Api Success: ', result); // Check for the presence of reasonCode and message fields if ("reasonCode" in result && "message" in result) { console.log("Authentication Api Error: ", result); callback({ status: 400, error: true, data: result, message: "Something went wrong!!", }); } else { console.log("Authentication Api Success: ", result); callback({ status: 200, error: false, data: result, message: "Authentication Successful!!!", }); } }) .catch((error) => { // If an error is thrown in any of the previous blocks, it will be caught here console.error("Authentication Api Error: ", error); // Optionally, call the callback with an error if not already done // callback({ error: true, message: 'Something went wrong, please try again!' }); // Since we're handling specific errors earlier, this catch might only be for unexpected errors }); } export function postMessages(obj: any): void { console.log("========> coming object",obj); let sipConfigs: any = {}; // Assuming sipConfigs is declared elsewhere if (Object.keys(sipConfigs).length === 0 && obj?.parameter?.authData){ sipConfigs = obj.parameter.authData; } else{ sipConfigs = obj.parameter.sipConfig; } console.log("=======>sip configs",sipConfigs) switch (obj.action) { case "login": if (typeof obj.parameter.clientCallbackFunction === "function") { if (sipConfigs.uriFs !== null && sipConfigs.uriFs !== undefined) { connect_useragent( obj.parameter.extension, sipConfigs.uriFs, sipConfigs.extensionPassword, sipConfigs.wssFs, sipConfigs.enabledSipLogs, obj.parameter.clientCallbackFunction ); callbackFunction = obj.parameter.clientCallbackFunction; // Assuming callbackFunction is declared elsewhere } else { error( "invalidState", obj.parameter.extension, "Server configurations not fetched ", obj.parameter.clientCallbackFunction ); } } break; case "logout": loader3(obj.parameter.clientCallbackFunction); break; case "makeCall": initiate_call( obj.parameter.calledNumber, obj.parameter.Destination_Number, obj.parameter.callType, obj.parameter.authData, obj.parameter.clientCallbackFunction, "OUT", ); break; case "SST": blind_transfer( obj.parameter.numberToTransfer, obj.parameter.clientCallbackFunction, obj.parameter.dialogId ); break; case "SST_Queue": blind_transfer_queue( obj.parameter.numberToTransfer, obj.parameter.queue, obj.parameter.queueType, obj.parameter.clientCallbackFunction, obj.parameter.dialogId ); break; case "makeConsult": makeConsultCall( obj.parameter.numberToConsult, obj.parameter.clientCallbackFunction ); break; case "makeConsultQueue": makeConsultCall_queue( obj.parameter.numberToTransfer, obj.parameter.queue, obj.parameter.queueType, obj.parameter.clientCallbackFunction, ); // console.log('Freeswitch do not support makeConsult currently'); break; case "consultTransfer": makeConsultTransferCall(obj.parameter.clientCallbackFunction); break; case "silentMonitor": console.log("Freeswitch do not support silentMonitor currently"); break; case "answerCall": respond_call( obj.parameter.clientCallbackFunction, obj.parameter.dialogId, obj.parameter.answerCalltype, ); break; case "releaseCall": terminate_call(obj.parameter.dialogId); break; case "rejectCall": console.log("Freeswitch do not support rejectCall currently"); break; case "closeCall": console.log("Freeswitch do not support closeCall currently"); break; case "end_call": console.log(obj); break; case "holdCall": phone_hold(obj.parameter.clientCallbackFunction, obj.parameter.dialogId); break; case "retrieveCall": phone_unhold( obj.parameter.clientCallbackFunction, obj.parameter.dialogId ); break; case "mute_call": phone_mute(obj.parameter.clientCallbackFunction, obj.parameter.dialogId); break; case "unmute_call": phone_unmute( obj.parameter.clientCallbackFunction, obj.parameter.dialogId ); break; case "conferenceCall": console.log("Freeswitch do not support conferenceCall currently"); break; case "makeNotReadyWithReason": console.log("Freeswitch do not support makeNotReadyWithReason currently"); break; case "makeReady": console.log("Freeswitch do not support makeReady currently"); break; case "makeWorkReady": console.log("Freeswitch do not support makeWorkReady currently"); break; case "getDialog": console.log("Freeswitch do not support getDialog currently"); break; case "getWrapUpReasons": console.log("Freeswitch do not support getWrapUpReasons currently"); break; case "updateCallVariableData": console.log("Freeswitch do not support updateCallVariableData currently"); break; case "updateWrapupData": console.log("Freeswitch do not support updateWrapupData currently"); break; case "acceptCall": console.log("Freeswitch do not support updateWrapupData currently"); break; case "dropParticipant": console.log("Freeswitch do not support dropParticipant currently"); break; case "bargeIn": console.log("Freeswitch do not support bargeIn currently"); break; case "SendDtmf": sendDtmf( obj.parameter.message, obj.parameter.dialogId, obj.parameter.clientCallbackFunction ); break; case "team_agent_update_status": console.log(obj); break; case "team_agent_update_state": console.log(obj); break; case "team_agent_update_reg": console.log(obj); break; case "getState": console.log("Freeswitch do not support getState currently"); break; case "getNotReadyLogoutReasons": console.log( "Freeswitch do not support getNotReadyLogoutReasons currently" ); break; case "getTeamUsers": console.log("Freeswitch do not support getTeamUsers currently"); break; case "convertCall": callConvert( obj.parameter.dialogId, obj.parameter.clientCallbackFunction, obj.parameter.streamType, obj.parameter.streamStatus, ); break; case "conference_consult": initiate_consult_Conference( obj.parameter.dialogId, obj.parameter.clientCallbackFunction, ); break; } } function callConvert(dialogId:any, callback:any, streamType:any, streamStatus:any) { var res = lockFunction("callConvert", 500); // --- seconds cooldown if (!res) return; const undefinedParams = checkUndefinedParams(callConvert, [ streamType, streamStatus, callback, dialogId, ]); if (undefinedParams.length > 0) { error( "generalError", loginid, `Error: The following parameter(s) are undefined or null or empty: ${undefinedParams.join( ", ", )}`, callback, ); return; } var index = getCallIndex(dialogId); if (index !== -1) { sessionall = calls[index].session; } if (!sessionall) { error("invalidState", loginid, "invalid action ConvertCall", callback); return; } let peer = sessionall.sessionDescriptionHandler.peerConnection; let senders = peer.getSenders(); if (!senders.length) return; var videoTrackcheck = false; const sysdate = new Date(); if (streamStatus === "off") { senders.forEach((sender:any) => { if (sender.track && sender.track.kind === "video") { sender.track.stop(); } }); setupRemoteMedia(sessionall,callback); generateConversionEvent(dialogId, streamType, streamStatus, callback); return; } senders.forEach(async (sender:any) => { if ( sender && sender.track && sender.track.kind && sender.track.kind === "video" ) { videoTrackcheck = true; if (sender.track.readyState === "live") { sender.track.stop(); } if (streamType === "video") { await navigator.mediaDevices .getUserMedia({ video: true }) .then(async (videoStream) => { let videoTrack = videoStream.getVideoTracks()[0]; await sender.replaceTrack(videoTrack); setupRemoteMedia(sessionall,callback); }); } else if (streamType === "screenshare") { await navigator.mediaDevices .getDisplayMedia({ video: true }) .then(async (videoStream) => { let videoTrack = videoStream.getVideoTracks()[0]; await sender.replaceTrack(videoTrack); setupRemoteMedia(sessionall,callback); }); } generateConversionEvent(dialogId, streamType, streamStatus, callback); } }); if (!videoTrackcheck) { sendingReInvite(dialogId, callback, streamType); } } interface SessionDescriptionHandlerOption { constraints:{ audio:any, video:any }; // Using the built-in MediaStreamConstraints interface for constraints offerOptions: { iceRestart: boolean; offerToReceiveAudio: boolean; offerToReceiveVideo: boolean; }; } function sendingReInvite(dialogId: string, callback: (data: any) => void, streamType:any ): void { const res = lockFunction("sendingReInvite", 1000); // seconds cooldown if (!res) return; const index = getCallIndex(dialogId); let sessionall:any; if (index !== -1) { sessi