@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
text/typescript
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