mirror of
https://github.com/RocketChat/Rocket.Chat.git
synced 2025-12-28 06:47:25 +00:00
[IMPROVE] Unify voip streams into single stream (#25108)
This commit is contained in:
parent
2192b91b0a
commit
4bc080b6cb
2
.gitignore
vendored
2
.gitignore
vendored
@ -40,3 +40,5 @@ yarn-error.log*
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
.nvmrc
|
||||
@ -39,7 +39,7 @@ export class QueueAggregator {
|
||||
}
|
||||
|
||||
private updateQueueInfo(queueName: string, queuedCalls: number): void {
|
||||
if (!this.currentQueueMembershipStatus[queueName]) {
|
||||
if (!this.currentQueueMembershipStatus?.[queueName]) {
|
||||
// something is wrong. Queue is not found in the membership details.
|
||||
return;
|
||||
}
|
||||
@ -48,14 +48,14 @@ export class QueueAggregator {
|
||||
|
||||
setMembership(subscription: IQueueMembershipSubscription): void {
|
||||
this.extension = subscription.extension;
|
||||
for (let i = 0; i < subscription.queues.length; i++) {
|
||||
const queue = subscription.queues[i];
|
||||
|
||||
subscription.queues.forEach((queue) => {
|
||||
const queueInfo: IQueueInfo = {
|
||||
queueName: queue.name,
|
||||
callsInQueue: 0,
|
||||
};
|
||||
this.currentQueueMembershipStatus[queue.name] = queueInfo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
queueJoined(joiningDetails: { queuename: string; callerid: { id: string }; queuedcalls: string }): void {
|
||||
@ -77,7 +77,7 @@ export class QueueAggregator {
|
||||
|
||||
memberRemoved(queue: { queuename: string; queuedcalls: string }): void {
|
||||
// current user is removed from the queue which has queue count |queuedcalls|
|
||||
if (!this.currentQueueMembershipStatus[queue.queuename]) {
|
||||
if (!this.currentQueueMembershipStatus?.[queue.queuename]) {
|
||||
// something is wrong. Queue is not found in the membership details.
|
||||
return;
|
||||
}
|
||||
@ -89,15 +89,11 @@ export class QueueAggregator {
|
||||
}
|
||||
|
||||
getCallWaitingCount(): number {
|
||||
let totalCallWaitingCount = 0;
|
||||
Object.entries(this.currentQueueMembershipStatus).forEach(([, value]) => {
|
||||
totalCallWaitingCount += value.callsInQueue;
|
||||
});
|
||||
return totalCallWaitingCount;
|
||||
return Object.entries(this.currentQueueMembershipStatus).reduce((acc, [_, value]) => acc + value.callsInQueue, 0);
|
||||
}
|
||||
|
||||
getCurrentQueueName(): string {
|
||||
if (this.currentlyServing.queueInfo) {
|
||||
if (this.currentlyServing?.queueInfo) {
|
||||
return this.currentlyServing.queueInfo.queueName;
|
||||
}
|
||||
|
||||
@ -105,7 +101,7 @@ export class QueueAggregator {
|
||||
}
|
||||
|
||||
callRinging(queueInfo: { queuename: string; callerid: { id: string; name: string } }): void {
|
||||
if (!this.currentQueueMembershipStatus[queueInfo.queuename]) {
|
||||
if (!this.currentQueueMembershipStatus?.[queueInfo.queuename]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import type { IVoipRoom, IUser } from '@rocket.chat/core-typings';
|
||||
import { ICallerInfo } from '@rocket.chat/core-typings';
|
||||
import type { IVoipRoom, IUser, VoipEventDataSignature } from '@rocket.chat/core-typings';
|
||||
import {
|
||||
ICallerInfo,
|
||||
isVoipEventAgentCalled,
|
||||
isVoipEventAgentConnected,
|
||||
isVoipEventCallerJoined,
|
||||
isVoipEventQueueMemberAdded,
|
||||
isVoipEventQueueMemberRemoved,
|
||||
isVoipEventCallAbandoned,
|
||||
} from '@rocket.chat/core-typings';
|
||||
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
|
||||
import { useSetModal, useRoute, useUser, useSetting, useEndpoint, useStream } from '@rocket.chat/ui-contexts';
|
||||
import { Random } from 'meteor/random';
|
||||
@ -65,90 +73,54 @@ export const CallProvider: FC = ({ children }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleAgentCalled = async (queue: {
|
||||
queuename: string;
|
||||
callerId: { id: string; name: string };
|
||||
queuedcalls: string;
|
||||
}): Promise<void> => {
|
||||
queueAggregator.callRinging({ queuename: queue.queuename, callerid: queue.callerId });
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
const handleEventReceived = async (event: VoipEventDataSignature): Promise<void> => {
|
||||
if (isVoipEventAgentCalled(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.callRinging({ queuename: data.queue, callerid: data.callerId });
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
return;
|
||||
}
|
||||
if (isVoipEventAgentConnected(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.callPickedup({ queuename: data.queue, queuedcalls: data.queuedCalls, waittimeinqueue: data.waitTimeInQueue });
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
return;
|
||||
}
|
||||
if (isVoipEventCallerJoined(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.queueJoined({ queuename: data.queue, callerid: data.callerId, queuedcalls: data.queuedCalls });
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
return;
|
||||
}
|
||||
if (isVoipEventQueueMemberAdded(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.memberAdded({ queuename: data.queue, queuedcalls: data.queuedCalls });
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
return;
|
||||
}
|
||||
if (isVoipEventQueueMemberRemoved(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.memberRemoved({ queuename: data.queue, queuedcalls: data.queuedCalls });
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
return;
|
||||
}
|
||||
if (isVoipEventCallAbandoned(event)) {
|
||||
const { data } = event;
|
||||
queueAggregator.queueAbandoned({ queuename: data.queue, queuedcallafterabandon: data.queuedCallAfterAbandon });
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn('Unknown event received');
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/agentcalled`, handleAgentCalled);
|
||||
}, [subscribeToNotifyUser, user, voipEnabled, queueAggregator]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleQueueJoined = async (joiningDetails: {
|
||||
queuename: string;
|
||||
callerid: { id: string };
|
||||
queuedcalls: string;
|
||||
}): Promise<void> => {
|
||||
queueAggregator.queueJoined(joiningDetails);
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/callerjoined`, handleQueueJoined);
|
||||
}, [subscribeToNotifyUser, user, voipEnabled, queueAggregator]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleAgentConnected = (queue: { queuename: string; queuedcalls: string; waittimeinqueue: string }): void => {
|
||||
queueAggregator.callPickedup(queue);
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/agentconnected`, handleAgentConnected);
|
||||
}, [queueAggregator, subscribeToNotifyUser, user, voipEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleMemberAdded = (queue: { queuename: string; queuedcalls: string }): void => {
|
||||
queueAggregator.memberAdded(queue);
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/queuememberadded`, handleMemberAdded);
|
||||
}, [queueAggregator, subscribeToNotifyUser, user, voipEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleMemberRemoved = (queue: { queuename: string; queuedcalls: string }): void => {
|
||||
queueAggregator.memberRemoved(queue);
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/queuememberremoved`, handleMemberRemoved);
|
||||
}, [queueAggregator, subscribeToNotifyUser, user, voipEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleCallAbandon = (queue: { queuename: string; queuedcallafterabandon: string }): void => {
|
||||
queueAggregator.queueAbandoned(queue);
|
||||
setQueueName(queueAggregator.getCurrentQueueName());
|
||||
setQueueCounter(queueAggregator.getCallWaitingCount());
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/callabandoned`, handleCallAbandon);
|
||||
}, [queueAggregator, subscribeToNotifyUser, user, voipEnabled]);
|
||||
return subscribeToNotifyUser(`${user._id}/voip.events`, handleEventReceived);
|
||||
}, [subscribeToNotifyUser, user, queueAggregator, voipEnabled]);
|
||||
|
||||
// This was causing event duplication before, so we'll leave this here for now
|
||||
useEffect(() => {
|
||||
if (!voipEnabled || !user || !queueAggregator) {
|
||||
return;
|
||||
@ -159,7 +131,7 @@ export const CallProvider: FC = ({ children }) => {
|
||||
openWrapUpModal();
|
||||
};
|
||||
|
||||
return subscribeToNotifyUser(`${user._id}/call.callerhangup`, handleCallHangup);
|
||||
return subscribeToNotifyUser(`${user._id}/call.hangup`, handleCallHangup);
|
||||
}, [openWrapUpModal, queueAggregator, subscribeToNotifyUser, user, voipEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -278,23 +278,13 @@ export class ListenersModule {
|
||||
service.onEvent('banner.enabled', (bannerId): void => {
|
||||
notifications.notifyLoggedInThisInstance('banner-changed', { bannerId });
|
||||
});
|
||||
service.onEvent('queue.agentcalled', (userId, queuename, callerId): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'agentcalled', { queuename, callerId });
|
||||
|
||||
service.onEvent('voip.events', (userId, data): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'voip.events', data);
|
||||
});
|
||||
service.onEvent('queue.agentconnected', (userId, queuename: string, queuedcalls: string, waittimeinqueue: string): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'agentconnected', { queuename, queuedcalls, waittimeinqueue });
|
||||
});
|
||||
service.onEvent('queue.callerjoined', (userId, queuename, callerid, queuedcalls): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'callerjoined', { queuename, callerid, queuedcalls });
|
||||
});
|
||||
service.onEvent('queue.queuememberadded', (userId, queuename: string, queuedcalls: string): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'queuememberadded', { queuename, queuedcalls });
|
||||
});
|
||||
service.onEvent('queue.queuememberremoved', (userId, queuename: string, queuedcalls: string): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'queuememberremoved', { queuename, queuedcalls });
|
||||
});
|
||||
service.onEvent('queue.callabandoned', (userId, queuename: string, queuedcallafterabandon: string): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'callabandoned', { queuename, queuedcallafterabandon });
|
||||
|
||||
service.onEvent('call.callerhangup', (userId, data): void => {
|
||||
notifications.notifyUserInThisInstance(userId, 'call.hangup', data);
|
||||
});
|
||||
|
||||
service.onEvent('notify.desktop', (uid, notification): void => {
|
||||
|
||||
@ -344,6 +344,12 @@ export function initWatchers(models: IModelsParam, broadcast: BroadcastCallback,
|
||||
// TODO: Prevent flood from database on username change, what causes changes on all past messages from that user
|
||||
// and most of those messages are not loaded by the clients.
|
||||
watch<IUser>(Users, ({ clientAction, id, data, diff, unset }) => {
|
||||
// LivechatCount is updated each time an agent is routed to a chat. This prop is not used on the UI so we don't need
|
||||
// to broadcast events originated by it when it's the only update on the user
|
||||
if (diff && Object.keys(diff).length === 1 && 'livechatCount' in diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
broadcast('watch.users', { clientAction, data, diff, unset, id });
|
||||
});
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import type {
|
||||
IInvite,
|
||||
IWebdavAccount,
|
||||
ICustomSound,
|
||||
VoipEventDataSignature,
|
||||
} from '@rocket.chat/core-typings';
|
||||
|
||||
import { AutoUpdateRecord } from '../types/IMeteor';
|
||||
@ -123,12 +124,10 @@ export type EventSignatures = {
|
||||
diff?: undefined | Record<string, any>;
|
||||
id: string;
|
||||
}): void;
|
||||
'queue.agentcalled'(userid: string, queuename: string, callerid: Record<string, string>): void;
|
||||
'queue.agentconnected'(userid: string, queuename: string, queuedcalls: string, waittimeinqueue: string): void;
|
||||
'queue.callerjoined'(userid: string, queuename: string, callerid: Record<string, string>, queuedcalls: string): void;
|
||||
'queue.queuememberadded'(userid: string, queuename: string, queuedcalls: string): void;
|
||||
'queue.queuememberremoved'(userid: string, queuename: string, queuedcalls: string): void;
|
||||
'queue.callabandoned'(userid: string, queuename: string, queuedcallafterabandon: string): void;
|
||||
|
||||
// Send all events from here
|
||||
'voip.events'(userId: string, data: VoipEventDataSignature): void;
|
||||
'call.callerhangup'(userId: string, data: { roomId: string }): void;
|
||||
'watch.pbxevents'(data: { clientAction: ClientAction; data: Partial<IPbxEvent>; id: string }): void;
|
||||
'connector.statuschanged'(enabled: boolean): void;
|
||||
};
|
||||
|
||||
@ -28,7 +28,7 @@ import { VoipRoomsRaw } from '../../../app/models/server/raw/VoipRooms';
|
||||
import { PbxEventsRaw } from '../../../app/models/server/raw/PbxEvents';
|
||||
import { sendMessage } from '../../../app/lib/server/functions/sendMessage';
|
||||
import { FindVoipRoomsParams } from './internalTypes';
|
||||
import { Notifications } from '../../../app/notifications/server';
|
||||
import { api } from '../../sdk/api';
|
||||
|
||||
export class OmnichannelVoipService extends ServiceClassInternal implements IOmnichannelVoipService {
|
||||
protected name = 'omnichannel-voip';
|
||||
@ -80,8 +80,7 @@ export class OmnichannelVoipService extends ServiceClassInternal implements IOmn
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`Notifying agent ${agent._id} of hangup on room ${currentRoom._id}`);
|
||||
// TODO evalute why this is 'notifyUserInThisInstance'
|
||||
Notifications.notifyUserInThisInstance(agent._id, 'call.callerhangup', { roomId: currentRoom._id });
|
||||
api.broadcast('call.callerhangup', agent._id, { roomId: currentRoom._id });
|
||||
}
|
||||
|
||||
private async processAgentDisconnect(extension: string): Promise<void> {
|
||||
|
||||
@ -89,8 +89,8 @@ export class ContinuousMonitor extends Command {
|
||||
|
||||
async processQueueMembershipChange(event: IQueueMemberAdded | IQueueMemberRemoved): Promise<void> {
|
||||
const extension = event.interface.toLowerCase().replace('pjsip/', '');
|
||||
const queueName = event.queue;
|
||||
const queueDetails = await this.getQueueDetails(queueName);
|
||||
const { queue } = event;
|
||||
const queueDetails = await this.getQueueDetails(queue);
|
||||
const { calls } = queueDetails;
|
||||
const user = await this.users.findOneByExtension(extension, {
|
||||
projection: {
|
||||
@ -101,9 +101,9 @@ export class ContinuousMonitor extends Command {
|
||||
});
|
||||
if (user) {
|
||||
if (isIQueueMemberAddedEvent(event)) {
|
||||
api.broadcast(`queue.queuememberadded`, user._id, queueName, calls);
|
||||
api.broadcast(`voip.events`, user._id, { data: { queue, queuedCalls: calls }, event: 'queue-member-added' });
|
||||
} else if (isIQueueMemberRemovedEvent(event)) {
|
||||
api.broadcast(`queue.queuememberremoved`, user._id, queueName, calls);
|
||||
api.broadcast(`voip.events`, user._id, { event: 'queue-member-removed', data: { queue, queuedCalls: calls } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,7 +130,8 @@ export class ContinuousMonitor extends Command {
|
||||
name: event.calleridname,
|
||||
};
|
||||
|
||||
api.broadcast('queue.agentcalled', user._id, event.queue, callerId);
|
||||
api.broadcast('voip.events', user._id, { event: 'agent-called', data: { callerId, queue: event.queue } });
|
||||
// api.broadcast('queue.agentcalled', user._id, event.queue, callerId);
|
||||
}
|
||||
|
||||
async storePbxEvent(event: IQueueEvent | IContactStatus, eventName: string): Promise<void> {
|
||||
@ -185,7 +186,7 @@ export class ContinuousMonitor extends Command {
|
||||
await this.storePbxEvent(event, 'QueueCallerJoin');
|
||||
this.logger.debug(`Broadcasting event queue.callerjoined to ${members.length} agents on queue ${event.queue}`);
|
||||
members.forEach((m) => {
|
||||
api.broadcast('queue.callerjoined', m, event.queue, callerId, event.count);
|
||||
api.broadcast('voip.events', m, { event: 'caller-joined', data: { callerId, queue: event.queue, queuedCalls: event.count } });
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -194,7 +195,7 @@ export class ContinuousMonitor extends Command {
|
||||
await this.storePbxEvent(event, 'QueueCallerAbandon');
|
||||
this.logger.debug(`Broadcasting event queue.callabandoned to ${members.length} agents on queue ${event.queue}`);
|
||||
members.forEach((m) => {
|
||||
api.broadcast('queue.callabandoned', m, event.queue, calls);
|
||||
api.broadcast('voip.events', m, { event: 'call-abandoned', data: { queue: event.queue, queuedCallAfterAbandon: calls } });
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -205,7 +206,10 @@ export class ContinuousMonitor extends Command {
|
||||
this.logger.debug(`Broadcasting event queue.agentconnected to ${members.length} agents on queue ${event.queue}`);
|
||||
members.forEach((m) => {
|
||||
// event.holdtime signifies wait time in the queue.
|
||||
api.broadcast('queue.agentconnected', m, event.queue, calls, event.holdtime);
|
||||
api.broadcast('voip.events', m, {
|
||||
event: 'agent-connected',
|
||||
data: { queue: event.queue, queuedCalls: calls, waitTimeInQueue: event.holdtime },
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@ -97,8 +97,6 @@ export * from './IOmnichannelAgent';
|
||||
export * from './OmichannelRoutingConfig';
|
||||
export * from './IVoipExtension';
|
||||
export * from './voip';
|
||||
export * from './voip/VoIPUserConfiguration';
|
||||
export * from './voip/VoIpCallerInfo';
|
||||
export * from './ACDQueues';
|
||||
export * from './IVoipConnectorResult';
|
||||
export * from './IVoipServerConfig';
|
||||
|
||||
54
packages/core-typings/src/voip/IVoipClientEvents.ts
Normal file
54
packages/core-typings/src/voip/IVoipClientEvents.ts
Normal file
@ -0,0 +1,54 @@
|
||||
export type VoipPropagatedEvents =
|
||||
| 'agentcalled'
|
||||
| 'agentconnected'
|
||||
| 'callerjoined'
|
||||
| 'queuememberadded'
|
||||
| 'queuememberremoved'
|
||||
| 'callabandoned';
|
||||
|
||||
export type VoipEventDataSignature =
|
||||
| {
|
||||
event: 'agent-called';
|
||||
data: { callerId: { id: string; name: string }; queue: string };
|
||||
}
|
||||
| {
|
||||
event: 'agent-connected';
|
||||
data: { queue: string; queuedCalls: string; waitTimeInQueue: string };
|
||||
}
|
||||
| {
|
||||
event: 'caller-joined';
|
||||
data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string };
|
||||
}
|
||||
| {
|
||||
event: 'queue-member-added';
|
||||
data: { queue: string; queuedCalls: string };
|
||||
}
|
||||
| {
|
||||
event: 'queue-member-removed';
|
||||
data: { queue: string; queuedCalls: string };
|
||||
}
|
||||
| {
|
||||
event: 'call-abandoned';
|
||||
data: { queuedCallAfterAbandon: string; queue: string };
|
||||
};
|
||||
|
||||
export const isVoipEventAgentCalled = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'agent-called'; data: { callerId: { id: string; name: string }; queue: string } } => data.event === 'agent-called';
|
||||
export const isVoipEventAgentConnected = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'agent-connected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } =>
|
||||
data.event === 'agent-connected';
|
||||
export const isVoipEventCallerJoined = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'caller-joined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string } } =>
|
||||
data.event === 'caller-joined';
|
||||
export const isVoipEventQueueMemberAdded = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'queue-member-added'; data: { queue: string; queuedCalls: string } } => data.event === 'queue-member-added';
|
||||
export const isVoipEventQueueMemberRemoved = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'queue-member-removed'; data: { queue: string; queuedCalls: string } } => data.event === 'queue-member-removed';
|
||||
export const isVoipEventCallAbandoned = (
|
||||
data: VoipEventDataSignature,
|
||||
): data is { event: 'call-abandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'call-abandoned';
|
||||
@ -12,3 +12,6 @@ export * from './UserState';
|
||||
export * from './VoipClientEvents';
|
||||
export * from './VoipEvents';
|
||||
export * from './WorkflowTypes';
|
||||
export * from './IVoipClientEvents';
|
||||
export * from './VoIPUserConfiguration';
|
||||
export * from './VoIpCallerInfo';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user