* NTP-21033 | hotfix * Publish - @universal-call-sdk/adapter-ameyo@1.0.99 - @universal-call-sdk/common@1.0.50 - @universal-call-sdk/core@1.0.54 * NTP-21033 | hotfix * NTP-21033 | erorr logger * NTP-21033 | fix beep sound * Publish - @universal-call-sdk/adapter-ameyo@1.0.100 - @universal-call-sdk/common@1.0.51 - @universal-call-sdk/core@1.0.55 --------- Co-authored-by: varnit goyal <github.cicd@navi.com>
466 lines
17 KiB
TypeScript
466 lines
17 KiB
TypeScript
import IAdapter from "@universal-call-sdk/common/lib/Interfaces/IAdapter.ts";
|
|
import GenericObject from "@universal-call-sdk/common/lib/types/GenericObject.ts";
|
|
import {
|
|
AmeyoInitializationOptions,
|
|
CALL_STATES,
|
|
CallTransferData,
|
|
RequestKeys,
|
|
SipAccountInfo,
|
|
StateType
|
|
} from "./types.ts";
|
|
import MessagingType from "../types/MessagingType.ts";
|
|
import {
|
|
ameyoHangupUser,
|
|
attachOmniqueService,
|
|
autoSelectExtension,
|
|
getSipAccountInfo,
|
|
loginInAmeyo, maintainHeartbeat,
|
|
selectCampaign,
|
|
setAgentActive, setAgentOnBreak,
|
|
setAutoStatus,
|
|
getCampaignId, ameyoDisposeCall,
|
|
getAllAgentsForTransferCall, transferCallToAgent
|
|
} from "./api.ts";
|
|
import {
|
|
acceptSipCall,
|
|
loadCallOptions,
|
|
loadCredentials,
|
|
sipHangUp,
|
|
sipMuteCall,
|
|
sipRegister, sipUnmuteCall
|
|
} from "./assets/js/sip5ml.service.ts";
|
|
import registerEventProcessor from "./eventsProcessor.ts";
|
|
import MetricsProcessor from "@universal-call-sdk/common/lib/utils/metricsProcessor.ts";
|
|
import ClickStreamProcessor from "@universal-call-sdk/common/lib/utils/clickStreamProcessor.ts";
|
|
|
|
class AmeyoAdapter implements IAdapter {
|
|
private callbacks: {
|
|
onCallConnected: (data: StateType) => void;
|
|
onCallDisconnected: (data: StateType) => void;
|
|
onCallIncoming: (data: StateType) => void,
|
|
onAdapterReady: () => void,
|
|
onAgentAvailabilityChange: (isAgentAvailable: boolean) => void
|
|
onForcedLogout: () => void,
|
|
onLoginFailed: (err: GenericObject)=>void
|
|
onAgentsForCallTransfer: (data: GenericObject) => void
|
|
onCallTransferStatus: (data: GenericObject) => void;
|
|
};
|
|
private currentCallState: CALL_STATES;
|
|
private eventListenerUrl: string;
|
|
private baseUrl: string;
|
|
private sessionId: string;
|
|
private campaignId: string;
|
|
private userName: string;
|
|
private password: string;
|
|
private currentCallMetadata: GenericObject;
|
|
private sipAccountInfo: GenericObject;
|
|
private isAgentAvailable: boolean;
|
|
private clickStreamProcessor: ClickStreamProcessor ;
|
|
private metricProcessor: MetricsProcessor;
|
|
constructor(options: AmeyoInitializationOptions) {
|
|
|
|
if(document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', this._appendTags);
|
|
} else {
|
|
this._appendTags();
|
|
}
|
|
|
|
this.baseUrl = options.baseUrl;
|
|
this.eventListenerUrl = options.eventListenerUrl;
|
|
this.currentCallState = CALL_STATES.IDLE;
|
|
this.callbacks = {
|
|
onCallIncoming: () => {
|
|
},
|
|
onCallConnected: () => {
|
|
},
|
|
onCallDisconnected: () => {
|
|
},
|
|
onAdapterReady: () => {
|
|
},
|
|
onAgentAvailabilityChange: () => {
|
|
},
|
|
onForcedLogout: () => {
|
|
},
|
|
onAgentsForCallTransfer: () => {
|
|
},
|
|
onCallTransferStatus: () => {
|
|
},
|
|
onLoginFailed: ()=>{
|
|
|
|
}
|
|
};
|
|
this.sessionId = '';
|
|
this.userName = options.userName;
|
|
this.password = options.password;
|
|
this.campaignId = '';
|
|
this.sipAccountInfo = {};
|
|
this.currentCallMetadata = {};
|
|
this.isAgentAvailable = false;
|
|
this.clickStreamProcessor = {} as ClickStreamProcessor;
|
|
this.metricProcessor={} as MetricsProcessor;
|
|
window.BASE_AMEYO_URL = this.baseUrl;
|
|
window.AMEYO_LOGIN_URL = options.loginUrl
|
|
|
|
}
|
|
|
|
init(metricProcessor: MetricsProcessor, clickStreamProcessor: ClickStreamProcessor) {
|
|
window.addEventListener('message', this._registerMessageListener);
|
|
this._initializeAmeyo();
|
|
this.metricProcessor = metricProcessor;
|
|
this.clickStreamProcessor = clickStreamProcessor;
|
|
this.metricProcessor?.pushCounterMetric({metricName: 'initSdk', flow: "sdk_init_count"})
|
|
}
|
|
|
|
|
|
_initializeSipStack = ({accountName, userName, domain = "", password}: SipAccountInfo) => {
|
|
const domainOnly = domain?.split?.(':')?.[0];
|
|
const port = domain?.split?.(':')?.[1];
|
|
//initialize sip stack
|
|
loadCredentials({accountName, userName, domain, password});
|
|
loadCallOptions();
|
|
sipRegister({domain: domainOnly, port});
|
|
this.metricProcessor?.pushCounterMetric({metricName: 'sipStackInitialised',flow: 'ameyo-sip-stack_init_count'})
|
|
|
|
|
|
}
|
|
|
|
_initializeAmeyo = () => {
|
|
loginInAmeyo(this.userName?.toLowerCase(), this.password);
|
|
|
|
}
|
|
|
|
|
|
_getElementFromDomById = (elementId: string): HTMLElement | null => {
|
|
return document.getElementById(elementId);
|
|
};
|
|
|
|
_onListenForCorsBypassResponse = (payload: GenericObject) => {
|
|
if(payload?.data?.requestKey !== RequestKeys.AMEYO_HEARTBEAT && !payload?.data?.err) {
|
|
this.metricProcessor?.pushCounterMetric({
|
|
metricName: `ameyo-api-call-count`,
|
|
flow: 'api-call-count',
|
|
subFlow: payload?.data?.requestKey
|
|
})
|
|
this.metricProcessor?.pushHistogramMetric({
|
|
metricName: `ameyo-api-latency-${payload?.data?.requestKey}`,
|
|
flow: 'api-latency',
|
|
subFlow: payload?.data?.requestKey,
|
|
value: payload?.data?.time/1000 || 0 //converting to seconds
|
|
})
|
|
}
|
|
|
|
if(payload?.data?.err) {
|
|
this.metricProcessor?.pushCounterMetric({metricName: `ameyo-api-err-count`, flow: 'api-error-count', subFlow: payload?.data?.requestKey})
|
|
this.clickStreamProcessor?.sendClickStreamEvent({type: 'api-error', err: payload});
|
|
}
|
|
|
|
if (payload?.data?.requestKey === RequestKeys.AMEYO_LOGIN) {
|
|
if(payload?.data?.err) {
|
|
console.log('on login failed', payload?.data?.err);
|
|
this.callbacks.onLoginFailed(payload?.err);
|
|
}
|
|
const sessionId = payload?.data?.response?.userSessionInfo?.sessionId;
|
|
this.sessionId = sessionId;
|
|
getSipAccountInfo(sessionId, this.userName?.toLowerCase());
|
|
registerEventProcessor(this.eventListenerUrl, sessionId);
|
|
maintainHeartbeat(this.sessionId, window?.listenerName || '', 0);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.AMEYO_HEARTBEAT) {
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.SIP_ACCOUNT_INFO) {
|
|
const response = payload?.data?.response;
|
|
this._initializeSipStack({
|
|
accountName: response?.accountName,
|
|
userName: response?.userName,
|
|
domain: response?.domain,
|
|
password: response?.secret
|
|
});
|
|
this.sipAccountInfo = payload?.data?.response;
|
|
console.log('sip account info', this.sipAccountInfo)
|
|
getCampaignId(this.sessionId);
|
|
setAutoStatus(this.sessionId);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.GET_CAMPAIGN_ID) {
|
|
this.campaignId = payload?.data?.response?.campaignInfos?.[0]?.campaignId;
|
|
attachOmniqueService(this.sessionId, this.userName.toLowerCase(), this.campaignId);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.AMEYO_AVAILABLE) {
|
|
setAutoStatus(this.sessionId);
|
|
this.isAgentAvailable = true;
|
|
window.postMessage({type: 'onAmeyoAvailabiltyChange', data: true});
|
|
this.callbacks.onAgentAvailabilityChange(true)
|
|
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.OMNIQUEUE_SERVICE) {
|
|
selectCampaign(this.sessionId, this.userName.toLowerCase(), this.campaignId);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.SELECT_CAMPAIGN) {
|
|
autoSelectExtension(this.sessionId, this.userName.toLowerCase());
|
|
this.callbacks.onAdapterReady();
|
|
this.currentCallState = CALL_STATES.IDLE;
|
|
this.callbacks.onAgentAvailabilityChange(true);
|
|
window.postMessage({type: 'onAmeyoAvailabiltyChange', data: false},);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.AMEYO_ON_BREAK) {
|
|
setAutoStatus(this.sessionId);
|
|
this.isAgentAvailable = false;
|
|
this.callbacks.onAgentAvailabilityChange(false);
|
|
window.postMessage({type: 'onAmeyoAvailabiltyChange', data: false},);
|
|
}
|
|
if (payload?.data?.requestKey === RequestKeys.GET_AGENTS_FOR_CALL_TRANSFER) {
|
|
this.callbacks.onAgentsForCallTransfer(payload?.data?.response);
|
|
}
|
|
}
|
|
|
|
_registerMessageListener = async ({data}: GenericObject) => {
|
|
if(data?.type !== MessagingType.SET_RESPONSE_WITHOUT_CORS && data?.type !== MessagingType.GET_RESPONSE_WITHOUT_CORS) {
|
|
this.metricProcessor?.pushCounterMetric({
|
|
metricName: `ameyo-events-count`,
|
|
flow: 'api-events-count',
|
|
subFlow:`universal-call-sdk-${data?.type}`
|
|
})
|
|
}
|
|
|
|
if (data?.type === MessagingType.SET_RESPONSE_WITHOUT_CORS) {
|
|
this._onListenForCorsBypassResponse(data);
|
|
}
|
|
if (data?.type === MessagingType.ON_AMEYO_CALL_INCOMING) {
|
|
this.callbacks.onCallIncoming(data?.data);
|
|
this.currentCallState = CALL_STATES.CALL_INCOMING;
|
|
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data}
|
|
}
|
|
if (data?.type === MessagingType.ON_AMEYO_CALL_ACCEPTED) {
|
|
this.callbacks.onCallConnected(data?.data);
|
|
this.currentCallState = CALL_STATES.CALL_CONNECTED;
|
|
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data}
|
|
}
|
|
if (data?.type === MessagingType.ON_AMEYO_CALL_DISCONNECTED) {
|
|
this.callbacks.onCallDisconnected(data?.data);
|
|
this.currentCallState = CALL_STATES.CALL_DISCONNECTED;
|
|
ameyoHangupUser(this.sessionId, this.currentCallMetadata?.userCRTObjectId);
|
|
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data}
|
|
const audioElement =this._getElementFromDomById("beep") as HTMLAudioElement
|
|
audioElement?.play();
|
|
|
|
}
|
|
if (data?.type === MessagingType.ON_AMEYO_AGENT_ON_BREAK) {
|
|
this.isAgentAvailable = false;
|
|
this.callbacks.onAgentAvailabilityChange(false);
|
|
}
|
|
if (data?.type === MessagingType.ON_AMEYO_FORCED_LOGOUT) {
|
|
this.callbacks.onForcedLogout()
|
|
}
|
|
if(data?.type === MessagingType.ON_AMEYO_CALL_TRANSFER){
|
|
this.callbacks.onCallTransferStatus(data?.data);
|
|
}
|
|
};
|
|
|
|
registerOnCallIncoming(callback: (callState: StateType) => void) {
|
|
console.log('registerOnCallIncoming');
|
|
this.callbacks.onCallIncoming = callback;
|
|
}
|
|
|
|
registerOnCallConnected(callback: (callState: StateType) => void) {
|
|
console.log('registerOnCallConnected');
|
|
this.callbacks.onCallConnected = callback;
|
|
}
|
|
|
|
registerOnCallDisconnected(callback: (callState: StateType) => void) {
|
|
console.log('registerOnCallDisconnected');
|
|
this.callbacks.onCallDisconnected = callback;
|
|
}
|
|
|
|
registerOnAdapterReady(callback: () => void) {
|
|
console.log('registerOnAdapterReady');
|
|
this.callbacks.onAdapterReady = callback;
|
|
}
|
|
|
|
registerOnAgentAvailabilityChange(callback: (isAgentAvailable: boolean) => void) {
|
|
console.log('registerOnAgentAvailabilityChange');
|
|
this.callbacks.onAgentAvailabilityChange = callback;
|
|
}
|
|
|
|
registerOnForcedLogoutListener(callback: () => void) {
|
|
console.log('registerOnAgentAvailabilityChange');
|
|
this.callbacks.onForcedLogout = callback;
|
|
}
|
|
|
|
registerOnLoginFailedListener(callback: ()=>void) {
|
|
console.log('register on login failed');
|
|
this.callbacks.onLoginFailed = callback
|
|
}
|
|
|
|
registerOnAgentsForCallTransfer(callback: (data: GenericObject) => void) {
|
|
console.log('registerOnAgentsForCallTransfer');
|
|
this.callbacks.onAgentsForCallTransfer = callback;
|
|
}
|
|
|
|
registerOnCallTransferStatus(callback: (data: GenericObject) => void) {
|
|
this.callbacks.onCallTransferStatus = callback;
|
|
}
|
|
|
|
acceptCall() {
|
|
acceptSipCall();
|
|
}
|
|
|
|
rejectCall() {
|
|
sipHangUp();
|
|
localStorage.removeItem('revEngCustomerInfo');
|
|
}
|
|
|
|
disposeCall() {
|
|
ameyoDisposeCall(this.sessionId, this.campaignId, this.currentCallMetadata?.crtObjectId, this.currentCallMetadata?.userCRTObjectId);
|
|
}
|
|
|
|
setOnBreak() {
|
|
setAgentOnBreak(this.sessionId);
|
|
}
|
|
|
|
setAvailable() {
|
|
setAgentActive(this.sessionId);
|
|
}
|
|
|
|
muteCall() {
|
|
sipMuteCall();
|
|
}
|
|
|
|
unmuteCall() {
|
|
sipUnmuteCall();
|
|
}
|
|
|
|
getAgentAvailability() {
|
|
return this.isAgentAvailable;
|
|
}
|
|
|
|
getLatestCallState() {
|
|
return this.currentCallState;
|
|
}
|
|
|
|
getAvailableAgentsForCallTransfer() {
|
|
getAllAgentsForTransferCall(this.sessionId);
|
|
}
|
|
|
|
transferCallToAgent(data: CallTransferData) {
|
|
transferCallToAgent(data,
|
|
this.sessionId,
|
|
this.currentCallMetadata?.crtObjectId,
|
|
this.currentCallMetadata?.userCRTObjectId,
|
|
this.campaignId);
|
|
}
|
|
|
|
private _appendTags: () => void = () => {
|
|
const script = document.createElement('script');
|
|
script.src = 'https://public-assets.np.navi-gi.in/jarvis/sip5ml.js'; // Assuming it's placed in the public folder
|
|
script.async = true;
|
|
document.head.appendChild(script);
|
|
const is_already_appended = document.querySelector('#audio_remote') && document.querySelector('#video_local') && document.querySelector('#video_remote') && document.querySelector('#ringtone') && document.querySelector('#ringbacktone') && document.querySelector('#dtmfTone');
|
|
if (is_already_appended) {
|
|
console.log('tags already appended skipping')
|
|
return;
|
|
}
|
|
type ElementAttributes = {
|
|
id?: string;
|
|
className?: string;
|
|
width?: string;
|
|
height?: string;
|
|
autoplay?: boolean;
|
|
muted?: boolean;
|
|
loop?: boolean;
|
|
src?: string;
|
|
style?: Partial<CSSStyleDeclaration>;
|
|
[key: string]: any;
|
|
};
|
|
|
|
function createElement(
|
|
tag: keyof HTMLElementTagNameMap,
|
|
attributes: ElementAttributes = {},
|
|
parent: HTMLElement = document.body
|
|
): HTMLElement {
|
|
const element = document.createElement(tag);
|
|
|
|
Object.keys(attributes).forEach((attr) => {
|
|
if (attr === 'style' && attributes.style) {
|
|
Object.assign(element.style, attributes.style);
|
|
} else if (attr in element) {
|
|
(element as any)[attr] = attributes[attr];
|
|
} else {
|
|
element.setAttribute(attr, attributes[attr]);
|
|
}
|
|
});
|
|
|
|
parent.appendChild(element);
|
|
return element;
|
|
}
|
|
|
|
createElement('audio', {
|
|
id: 'audio_remote',
|
|
autoplay: true,
|
|
});
|
|
|
|
createElement('video', {
|
|
className: 'video',
|
|
width: '100%',
|
|
height: '100%',
|
|
id: 'video_local',
|
|
autoplay: true,
|
|
muted: true,
|
|
style: {
|
|
opacity: '0',
|
|
display: 'none',
|
|
backgroundColor: '#000000',
|
|
webkitTransitionProperty: 'opacity',
|
|
webkitTransitionDuration: '2s',
|
|
},
|
|
});
|
|
|
|
createElement('video', {
|
|
className: 'video',
|
|
width: '100%',
|
|
height: '100%',
|
|
id: 'video_remote',
|
|
autoplay: true,
|
|
style: {
|
|
display: 'none',
|
|
opacity: '0',
|
|
backgroundColor: '#000000',
|
|
webkitTransitionProperty: 'opacity',
|
|
webkitTransitionDuration: '2s',
|
|
},
|
|
});
|
|
|
|
createElement('audio', {
|
|
id: 'ringtone',
|
|
loop: true,
|
|
src: 'https://public-assets.np.navi-gi.in/jarvis/ringtone.wav',
|
|
});
|
|
|
|
createElement('audio', {
|
|
id: 'ringbacktone',
|
|
loop: true,
|
|
src: 'https://public-assets.np.navi-gi.in/jarvis/ringbacktone.wav',
|
|
});
|
|
|
|
createElement('audio', {
|
|
id: 'dtmfTone',
|
|
src: 'https://public-assets.np.navi-gi.in/jarvis/dtmf.wav',
|
|
});
|
|
createElement("audio", {
|
|
id: "beep",
|
|
src: "https://public-assets.np.navi-gi.in/jarvis/beep.wav",
|
|
});
|
|
const onSipSetupReadyEvent = new CustomEvent('onSipSetupReady', {
|
|
detail: {message: 'Custom page loaded event triggered'}
|
|
});
|
|
|
|
script.onload = () => {
|
|
window.dispatchEvent(onSipSetupReadyEvent)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
export default AmeyoAdapter;
|