NTP-26125 | seperated tags

This commit is contained in:
Mayank Singh
2025-01-13 18:42:47 +05:30
parent 1259a3e468
commit d89f570148
6 changed files with 476 additions and 336 deletions

BIN
packages/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -15,7 +15,7 @@ class CallStateManager {
private readonly MAX_CONNECTION_ATTEMPTS = 3;
private connectionTimeout: ReturnType<typeof setTimeout> | null = null;
private sendConnectedMessage() {
public sendConnectedMessage() {
const customerInfo = this.state.customerInfo;
if (customerInfo?.callId || customerInfo?.phoneNumber) {
@@ -94,14 +94,6 @@ class CallStateManager {
data: this.state.customerInfo
});
break;
case 'connected':
if (!this.state.isConnected) {
this.state.isConnected = true;
this.sendConnectedMessage();
}
break;
case 'hungup':
this.handleCallDisconnect();
break;

View File

@@ -0,0 +1,156 @@
type TagConfig = {
id: string;
type: 'audio' | 'video';
attributes?: Record<string, string | boolean>;
styles?: Partial<CSSStyleDeclaration>;
}
class TagManager {
private static instance: TagManager;
private initialized: boolean = false;
private readonly scriptUrl = 'https://public-assets.np.navi-gi.in/jarvis/sip5ml.js';
private readonly tagConfigs: TagConfig[] = [
{
id: 'audio_remote',
type: 'audio',
attributes: {
autoplay: true
}
},
{
id: 'video_local',
type: 'video',
attributes: {
className: 'video',
width: '100%',
height: '100%',
autoplay: true,
muted: true
},
styles: {
opacity: '0',
display: 'none',
backgroundColor: '#000000',
webkitTransitionProperty: 'opacity',
webkitTransitionDuration: '2s'
}
},
{
id: 'video_remote',
type: 'video',
attributes: {
className: 'video',
width: '100%',
height: '100%',
autoplay: true
},
styles: {
opacity: '0',
display: 'none',
backgroundColor: '#000000',
webkitTransitionProperty: 'opacity',
webkitTransitionDuration: '2s'
}
},
{
id: 'ringtone',
type: 'audio',
attributes: {
loop: true,
src: 'https://public-assets.np.navi-gi.in/jarvis/ringtone.wav'
}
},
{
id: 'ringbacktone',
type: 'audio',
attributes: {
loop: true,
src: 'https://public-assets.np.navi-gi.in/jarvis/ringbacktone.wav'
}
},
{
id: 'dtmfTone',
type: 'audio',
attributes: {
src: 'https://public-assets.np.navi-gi.in/jarvis/dtmf.wav'
}
},
{
id: 'beep',
type: 'audio',
attributes: {
src: 'https://public-assets.np.navi-gi.in/jarvis/beep.wav'
}
}
];
private constructor() {}
public static getInstance(): TagManager {
if (!TagManager.instance) {
TagManager.instance = new TagManager();
}
return TagManager.instance;
}
private createTag(config: TagConfig): HTMLElement {
const element = document.createElement(config.type);
// Set attributes
if (config.attributes) {
Object.entries(config.attributes).forEach(([key, value]) => {
if (typeof value === 'boolean') {
if (value) element.setAttribute(key, '');
} else {
element.setAttribute(key, value);
}
});
}
// Set styles
if (config.styles) {
Object.assign(element.style, config.styles);
}
element.id = config.id;
return element;
}
private areTagsPresent(): boolean {
return this.tagConfigs.every(config => document.getElementById(config.id));
}
private appendScript(): Promise<void> {
return new Promise((resolve) => {
const script = document.createElement('script');
script.src = this.scriptUrl;
script.async = true;
script.onload = () => {
const event = new CustomEvent('onSipSetupReady', {
detail: { message: 'SIP setup loaded' }
});
window.dispatchEvent(event);
resolve();
};
document.head.appendChild(script);
});
}
public async initialize(): Promise<void> {
if (this.initialized || this.areTagsPresent()) {
console.log('Tags already initialized, skipping');
return;
}
this.tagConfigs.forEach(config => {
const element = this.createTag(config);
document.body.appendChild(element);
});
await this.appendScript();
this.initialized = true;
}
}
export const tagManager = TagManager.getInstance();

View File

@@ -1,50 +1,46 @@
import IAdapter from "@universal-call-sdk/common/lib/Interfaces/IAdapter.ts";
import GenericObject from "@universal-call-sdk/common/lib/types/GenericObject.ts";
import IAdapter from "@universal-call-sdk/common/lib/Interfaces/IAdapter";
import GenericObject from "@universal-call-sdk/common/lib/types/GenericObject";
import {
AmeyoInitializationOptions,
CALL_STATES,
CALL_STATES, CallbackFunctions,
CallTransferData,
RequestKeys,
SipAccountInfo,
StateType
} from "./types.ts";
import MessagingType from "../types/MessagingType.ts";
} from "./types";
import {
ameyoHangupUser,
attachOmniqueService,
autoSelectExtension,
getSipAccountInfo,
loginInAmeyo, maintainHeartbeat,
loginInAmeyo,
maintainHeartbeat,
selectCampaign,
setAgentActive, setAgentOnBreak,
setAgentActive,
setAgentOnBreak,
setAutoStatus,
getCampaignId, ameyoDisposeCall,
getAllAgentsForTransferCall, transferCallToAgent
} from "./api.ts";
getCampaignId,
ameyoDisposeCall,
getAllAgentsForTransferCall,
transferCallToAgent
} from "./api";
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";
sipRegister,
sipUnmuteCall
} from "./assets/js/sip5ml.service";
import registerEventProcessor from "./eventsProcessor";
import MetricsProcessor from "@universal-call-sdk/common/lib/utils/metricsProcessor";
import ClickStreamProcessor from "@universal-call-sdk/common/lib/utils/clickStreamProcessor";
import {tagManager} from "./htmlTagManager";
import {callStateManager} from "./callStateManager.ts";
import MessagingType from "../types/MessagingType.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 callbacks: CallbackFunctions;
private currentCallState: CALL_STATES;
private eventListenerUrl: string;
private baseUrl: string;
@@ -55,19 +51,24 @@ class AmeyoAdapter implements IAdapter {
private currentCallMetadata: GenericObject;
private sipAccountInfo: GenericObject;
private isAgentAvailable: boolean;
private clickStreamProcessor: ClickStreamProcessor ;
private clickStreamProcessor: ClickStreamProcessor;
private metricProcessor: MetricsProcessor;
constructor(options: AmeyoInitializationOptions) {
if(document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', this._appendTags);
} else {
this._appendTags();
}
this.validateInitializationOptions(options);
this.baseUrl = options.baseUrl;
this.eventListenerUrl = options.eventListenerUrl;
this.userName = options.userName;
this.password = options.password;
this.currentCallState = CALL_STATES.IDLE;
this.sessionId = '';
this.campaignId = '';
this.sipAccountInfo = {};
this.currentCallMetadata = {};
this.isAgentAvailable = false;
this.clickStreamProcessor = {} as ClickStreamProcessor;
this.metricProcessor = {} as MetricsProcessor;
this.callbacks = {
onCallIncoming: () => {
},
@@ -81,385 +82,364 @@ class AmeyoAdapter implements IAdapter {
},
onForcedLogout: () => {
},
onLoginFailed: () => {
},
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
this.setupGlobalVariables(options);
this.initializeTagManager();
}
init(metricProcessor: MetricsProcessor, clickStreamProcessor: ClickStreamProcessor) {
window.addEventListener('message', this._registerMessageListener);
this._initializeAmeyo();
private validateInitializationOptions(options: AmeyoInitializationOptions): void {
if (!options.baseUrl || !options.eventListenerUrl || !options.userName || !options.password || !options.loginUrl) {
throw new Error('Missing required initialization options');
}
}
private setupGlobalVariables(options: AmeyoInitializationOptions): void {
window.BASE_AMEYO_URL = this.baseUrl;
window.AMEYO_LOGIN_URL = options.loginUrl;
}
private initializeTagManager(): void {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => tagManager.initialize());
} else {
tagManager.initialize();
}
}
public init(metricProcessor: MetricsProcessor, clickStreamProcessor: ClickStreamProcessor): void {
window.addEventListener('message', this.handleMessage);
this.initializeAmeyo();
this.metricProcessor = metricProcessor;
this.clickStreamProcessor = clickStreamProcessor;
this.metricProcessor?.pushCounterMetric({metricName: 'initSdk', flow: "sdk_init_count"})
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);
private initializeSipStack = ({accountName, userName, domain = "", password}: SipAccountInfo): void => {
try {
const [domainOnly, port] = domain.split(':');
loadCredentials({accountName, userName, domain, password});
loadCallOptions();
sipRegister({domain: domainOnly, port});
this.metricProcessor?.pushCounterMetric({
metricName: 'sipStackInitialised',
flow: 'ameyo-sip-stack_init_count'
});
} catch (error) {
console.error('Error initializing SIP stack:', error);
this.handleError('SIP_INIT_ERROR', error);
}
};
_onListenForCorsBypassResponse = (payload: GenericObject) => {
if(payload?.data?.requestKey !== RequestKeys.AMEYO_HEARTBEAT && !payload?.data?.err) {
private initializeAmeyo = (): void => {
loginInAmeyo(this.userName.toLowerCase(), this.password);
};
private handleMessage = async ({data}: MessageEvent): Promise<void> => {
try {
if (this.shouldTrackMetrics(data.type)) {
this.trackEventMetrics(data.type);
}
switch (data.type) {
case MessagingType.SET_RESPONSE_WITHOUT_CORS:
await this.handleCorsBypassResponse(data);
break;
case MessagingType.ON_AMEYO_CALL_INCOMING:
this.handleCallIncoming(data);
break;
case MessagingType.ON_AMEYO_CALL_ACCEPTED:
this.handleCallAccepted(data);
break;
case MessagingType.ON_AMEYO_CALL_DISCONNECTED:
await this.handleCallDisconnected(data);
break;
case MessagingType.ON_AMEYO_AGENT_ON_BREAK:
this.handleAgentBreak();
break;
case MessagingType.ON_AMEYO_FORCED_LOGOUT:
this.callbacks.onForcedLogout();
break;
case MessagingType.ON_AMEYO_CALL_TRANSFER:
this.callbacks.onCallTransferStatus(data.data);
break;
}
} catch (error) {
console.error('Error handling message:', error);
this.handleError('MESSAGE_HANDLING_ERROR', error);
}
};
private shouldTrackMetrics(messageType: string): boolean {
return messageType !== MessagingType.SET_RESPONSE_WITHOUT_CORS &&
messageType !== MessagingType.GET_RESPONSE_WITHOUT_CORS;
}
private trackEventMetrics(messageType: string): void {
this.metricProcessor?.pushCounterMetric({
metricName: `ameyo-events-count`,
flow: 'api-events-count',
subFlow: `universal-call-sdk-${messageType}`
});
}
private handleError(type: string, error: any): void {
this.metricProcessor?.pushCounterMetric({
metricName: `ameyo-error-${type}`,
flow: 'error-count',
subFlow: type
});
this.clickStreamProcessor?.sendClickStreamEvent({
type: 'error',
error: error.message || error
});
}
private async handleCorsBypassResponse(payload: GenericObject): Promise<void> {
this.trackApiMetrics(payload);
switch (payload?.data?.requestKey) {
case RequestKeys.AMEYO_LOGIN:
await this.handleLoginResponse(payload);
break;
case RequestKeys.SIP_ACCOUNT_INFO:
await this.handleSipAccountInfo(payload);
break;
case RequestKeys.GET_CAMPAIGN_ID:
await this.handleCampaignId(payload);
break;
case RequestKeys.AMEYO_AVAILABLE:
this.handleAgentAvailable();
break;
case RequestKeys.OMNIQUEUE_SERVICE:
await this.handleOmniQueueService();
break;
case RequestKeys.SELECT_CAMPAIGN:
await this.handleCampaignSelection();
break;
case RequestKeys.AMEYO_ON_BREAK:
this.handleAgentBreak();
break;
case RequestKeys.GET_AGENTS_FOR_CALL_TRANSFER:
this.handleAgentsForTransfer(payload);
break;
}
}
private trackApiMetrics(payload: GenericObject): void {
if (payload?.data?.requestKey !== RequestKeys.AMEYO_HEARTBEAT && !payload?.data?.err) {
this.metricProcessor?.pushCounterMetric({
metricName: `ameyo-api-call-count`,
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
value: payload?.data?.time / 1000 || 0
});
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) {
if (payload?.data?.err) {
this.metricProcessor?.pushCounterMetric({
metricName: `ameyo-events-count`,
flow: 'api-events-count',
subFlow:`universal-call-sdk-${data?.type}`
})
metricName: 'ameyo-api-err-count',
flow: 'api-error-count',
subFlow: payload?.data?.requestKey
});
this.clickStreamProcessor?.sendClickStreamEvent({
type: 'api-error',
err: payload
});
}
}
private async handleLoginResponse(payload: GenericObject): Promise<void> {
if (payload?.data?.err) {
console.log('Login failed:', payload?.data?.err);
this.callbacks.onLoginFailed(payload?.err);
return;
}
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;
const sessionId = payload?.data?.response?.userSessionInfo?.sessionId;
this.sessionId = sessionId;
await getSipAccountInfo(sessionId, this.userName?.toLowerCase());
registerEventProcessor(this.eventListenerUrl, sessionId);
maintainHeartbeat(this.sessionId, window?.listenerName || '', 0);
}
registerOnCallConnected(callback: (callState: StateType) => void) {
console.log('registerOnCallConnected');
this.callbacks.onCallConnected = callback;
private async handleSipAccountInfo(payload: GenericObject): Promise<void> {
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)
await getCampaignId(this.sessionId);
await setAutoStatus(this.sessionId);
}
registerOnCallDisconnected(callback: (callState: StateType) => void) {
console.log('registerOnCallDisconnected');
this.callbacks.onCallDisconnected = callback;
private async handleCampaignId(payload: GenericObject): Promise<void> {
this.campaignId = payload?.data?.response?.campaignInfos?.[0]?.campaignId;
await attachOmniqueService(this.sessionId, this.userName.toLowerCase(), this.campaignId);
}
registerOnAdapterReady(callback: () => void) {
console.log('registerOnAdapterReady');
this.callbacks.onAdapterReady = callback;
private handleAgentAvailable(): void {
setAutoStatus(this.sessionId);
this.isAgentAvailable = true;
window.postMessage({type: 'onAmeyoAvailabiltyChange', data: true});
this.callbacks.onAgentAvailabilityChange(true);
}
registerOnAgentAvailabilityChange(callback: (isAgentAvailable: boolean) => void) {
console.log('registerOnAgentAvailabilityChange');
this.callbacks.onAgentAvailabilityChange = callback;
private async handleOmniQueueService(): Promise<void> {
await selectCampaign(this.sessionId, this.userName.toLowerCase(), this.campaignId);
}
registerOnForcedLogoutListener(callback: () => void) {
console.log('registerOnAgentAvailabilityChange');
this.callbacks.onForcedLogout = callback;
private async handleCampaignSelection(): Promise<void> {
await autoSelectExtension(this.sessionId, this.userName.toLowerCase());
this.callbacks.onAdapterReady();
this.currentCallState = CALL_STATES.IDLE;
this.callbacks.onAgentAvailabilityChange(true);
window.postMessage({type: 'onAmeyoAvailabiltyChange', data: false});
}
registerOnLoginFailedListener(callback: ()=>void) {
console.log('register on login failed');
this.callbacks.onLoginFailed = callback
private handleAgentBreak(): void {
this.isAgentAvailable = false;
this.callbacks.onAgentAvailabilityChange(false);
}
registerOnAgentsForCallTransfer(callback: (data: GenericObject) => void) {
console.log('registerOnAgentsForCallTransfer');
this.callbacks.onAgentsForCallTransfer = callback;
private handleAgentsForTransfer(payload: GenericObject): void {
this.callbacks.onAgentsForCallTransfer(payload?.data?.response);
}
registerOnCallTransferStatus(callback: (data: GenericObject) => void) {
this.callbacks.onCallTransferStatus = callback;
private handleCallIncoming(data: GenericObject): void {
this.callbacks.onCallIncoming(data?.data);
this.currentCallState = CALL_STATES.CALL_INCOMING;
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data};
}
acceptCall() {
private handleCallAccepted(data: GenericObject): void {
this.callbacks.onCallConnected(data?.data);
this.currentCallState = CALL_STATES.CALL_CONNECTED;
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data};
}
private async handleCallDisconnected(data: GenericObject): Promise<void> {
this.callbacks.onCallDisconnected(data?.data);
this.currentCallState = CALL_STATES.CALL_DISCONNECTED;
await ameyoHangupUser(this.sessionId, this.currentCallMetadata?.userCRTObjectId);
this.currentCallMetadata = {...this.currentCallMetadata, ...data?.data};
const audioElement = document.getElementById("beep") as HTMLAudioElement;
await audioElement?.play();
}
public acceptCall(): void {
acceptSipCall();
callStateManager.sendConnectedMessage();
}
rejectCall() {
public rejectCall(): void {
sipHangUp();
localStorage.removeItem('revEngCustomerInfo');
}
disposeCall() {
ameyoDisposeCall(this.sessionId, this.campaignId, this.currentCallMetadata?.crtObjectId, this.currentCallMetadata?.userCRTObjectId);
public disposeCall(): void {
ameyoDisposeCall(
this.sessionId,
this.campaignId,
this.currentCallMetadata?.crtObjectId,
this.currentCallMetadata?.userCRTObjectId
);
}
setOnBreak() {
public setOnBreak(): void {
setAgentOnBreak(this.sessionId);
}
setAvailable() {
public setAvailable(): void {
setAgentActive(this.sessionId);
}
muteCall() {
public muteCall(): void {
sipMuteCall();
}
unmuteCall() {
public unmuteCall(): void {
sipUnmuteCall();
}
getAgentAvailability() {
public getAgentAvailability(): boolean {
return this.isAgentAvailable;
}
getLatestCallState() {
public getLatestCallState(): CALL_STATES {
return this.currentCallState;
}
getAvailableAgentsForCallTransfer() {
public getAvailableAgentsForCallTransfer(): void {
getAllAgentsForTransferCall(this.sessionId);
}
transferCallToAgent(data: CallTransferData) {
transferCallToAgent(data,
public transferCallToAgent(data: CallTransferData): void {
transferCallToAgent(
data,
this.sessionId,
this.currentCallMetadata?.crtObjectId,
this.currentCallMetadata?.userCRTObjectId,
this.campaignId);
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;
};
// Event registration methods
public registerOnCallIncoming(callback: (callState: StateType) => void): void {
this.callbacks.onCallIncoming = callback;
}
function createElement(
tag: keyof HTMLElementTagNameMap,
attributes: ElementAttributes = {},
parent: HTMLElement = document.body
): HTMLElement {
const element = document.createElement(tag);
public registerOnCallConnected(callback: (callState: StateType) => void): void {
this.callbacks.onCallConnected = callback;
}
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]);
}
});
public registerOnCallDisconnected(callback: (callState: StateType) => void): void {
this.callbacks.onCallDisconnected = callback;
}
parent.appendChild(element);
return element;
}
public registerOnAdapterReady(callback: () => void): void {
this.callbacks.onAdapterReady = callback;
}
createElement('audio', {
id: 'audio_remote',
autoplay: true,
});
public registerOnAgentAvailabilityChange(callback: (isAgentAvailable: boolean) => void): void {
this.callbacks.onAgentAvailabilityChange = callback;
}
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',
},
});
public registerOnForcedLogoutListener(callback: () => void): void {
this.callbacks.onForcedLogout = callback;
}
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)
}
};
public registerOnLoginFailedListener(callback: () => void): void {
this.callbacks.onLoginFailed = callback;
}
public registerOnAgentsForCallTransfer(callback: (data: GenericObject) => void): void {
this.callbacks.onAgentsForCallTransfer = callback;
}
public registerOnCallTransferStatus(callback: (data: GenericObject) => void): void {
this.callbacks.onCallTransferStatus = callback;
}
}
export default AmeyoAdapter;

View File

@@ -64,6 +64,18 @@ export type SipAccountInfo = {
password: string
}
export type CallbackFunctions = {
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;
};
export enum CALL_STATES {
CALL_INCOMING = 'CALL_INCOMING',