From 7668139cf68bb90f827de1e85428be5e9aa3f325 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 7 Apr 2022 19:40:49 -0600 Subject: [PATCH 1/4] Unify voip streams into single stream --- .gitignore | 2 + .../meteor/client/lib/voip/QueueAggregator.ts | 20 ++- .../providers/CallProvider/CallProvider.tsx | 126 ++++++------------ .../definition/voip/IVoipClientEvents.ts | 33 +++++ .../modules/listeners/listeners.module.ts | 22 +-- .../modules/watchers/watchers.module.ts | 6 + apps/meteor/server/sdk/lib/Events.ts | 11 +- .../services/omnichannel-voip/service.ts | 5 +- .../asterisk/ami/ContinuousMonitor.ts | 20 +-- 9 files changed, 118 insertions(+), 127 deletions(-) create mode 100644 apps/meteor/definition/voip/IVoipClientEvents.ts diff --git a/.gitignore b/.gitignore index ba56f012004a..60b6012dfcad 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ yarn-error.log* !.yarn/releases !.yarn/sdks !.yarn/versions + +.nvmrc \ No newline at end of file diff --git a/apps/meteor/client/lib/voip/QueueAggregator.ts b/apps/meteor/client/lib/voip/QueueAggregator.ts index 676517eb1f86..b4e0efb11454 100644 --- a/apps/meteor/client/lib/voip/QueueAggregator.ts +++ b/apps/meteor/client/lib/voip/QueueAggregator.ts @@ -40,7 +40,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; } @@ -49,14 +49,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 { @@ -78,7 +78,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; } @@ -90,15 +90,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; } @@ -106,7 +102,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; } diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 06dc2e40033b..98797b874f54 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -8,6 +8,7 @@ import { getUserPreference } from '../../../app/utils/client'; import { IVoipRoom } from '../../../definition/IRoom'; import { IUser } from '../../../definition/IUser'; import { ICallerInfo } from '../../../definition/voip/ICallerInfo'; +import { VoipEventDataSignature } from '../../../definition/voip/IVoipClientEvents'; import { WrapUpCallModal } from '../../components/voip/modal/WrapUpCallModal'; import { CallContext, CallContextValue } from '../../contexts/CallContext'; import { useSetModal } from '../../contexts/ModalContext'; @@ -67,90 +68,51 @@ export const CallProvider: FC = ({ children }) => { return; } - const handleAgentCalled = async (queue: { - queuename: string; - callerId: { id: string; name: string }; - queuedcalls: string; - }): Promise => { - queueAggregator.callRinging({ queuename: queue.queuename, callerid: queue.callerId }); - setQueueName(queueAggregator.getCurrentQueueName()); - }; - - 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 => { - 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()); + const handleEventReceived = async ({ event, data }: VoipEventDataSignature): Promise => { + switch (event) { + case 'agentcalled': { + queueAggregator.callRinging({ queuename: data.queue, callerid: data.callerId }); + setQueueName(queueAggregator.getCurrentQueueName()); + break; + } + case 'agentconnected': { + queueAggregator.callPickedup({ queuename: data.queue, queuedcalls: data.queuedCalls, waittimeinqueue: data.waitTimeInQueue }); + setQueueName(queueAggregator.getCurrentQueueName()); + setQueueCounter(queueAggregator.getCallWaitingCount()); + break; + } + case 'callerjoined': { + queueAggregator.queueJoined({ queuename: data.queue, callerid: data.callerId, queuedcalls: data.queuedCalls }); + setQueueCounter(queueAggregator.getCallWaitingCount()); + break; + } + case 'queuememberadded': { + queueAggregator.memberAdded({ queuename: data.queue, queuedcalls: data.queuedCalls }); + setQueueName(queueAggregator.getCurrentQueueName()); + setQueueCounter(queueAggregator.getCallWaitingCount()); + break; + } + case 'queuememberremoved': { + queueAggregator.memberRemoved({ queuename: data.queue, queuedcalls: data.queuedCalls }); + setQueueCounter(queueAggregator.getCallWaitingCount()); + break; + } + case 'callabandoned': { + queueAggregator.queueAbandoned({ queuename: data.queue, queuedcallafterabandon: data.queuedCallAfterAbandon }); + setQueueName(queueAggregator.getCurrentQueueName()); + setQueueCounter(queueAggregator.getCallWaitingCount()); + break; + } + default: { + console.warn('Unknown event received', event); + } + } }; - 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; @@ -161,7 +123,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(() => { diff --git a/apps/meteor/definition/voip/IVoipClientEvents.ts b/apps/meteor/definition/voip/IVoipClientEvents.ts new file mode 100644 index 000000000000..fa4a04443d0b --- /dev/null +++ b/apps/meteor/definition/voip/IVoipClientEvents.ts @@ -0,0 +1,33 @@ +export type VoipPropagatedEvents = + | 'agentcalled' + | 'agentconnected' + | 'callerjoined' + | 'queuememberadded' + | 'queuememberremoved' + | 'callabandoned'; + +export type VoipEventDataSignature = + | { + event: 'agentcalled'; + data: { callerId: { id: string; name: string }; queue: string }; + } + | { + event: 'agentconnected'; + data: { queue: string; queuedCalls: string; waitTimeInQueue: string }; + } + | { + event: 'callerjoined'; + data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string }; + } + | { + event: 'queuememberadded'; + data: { queue: string; queuedCalls: string }; + } + | { + event: 'queuememberremoved'; + data: { queue: string; queuedCalls: string }; + } + | { + event: 'callabandoned'; + data: { queuedCallAfterAbandon: string; queue: string }; + }; diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index ceea604fb199..8bc59e9d0045 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -271,23 +271,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('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('voip.events', (userId, data): void => { + notifications.notifyUserInThisInstance(userId, 'voip.events', data); }); - 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 => { diff --git a/apps/meteor/server/modules/watchers/watchers.module.ts b/apps/meteor/server/modules/watchers/watchers.module.ts index b99e945cd112..3e933a6e7afc 100644 --- a/apps/meteor/server/modules/watchers/watchers.module.ts +++ b/apps/meteor/server/modules/watchers/watchers.module.ts @@ -342,6 +342,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(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 }); }); diff --git a/apps/meteor/server/sdk/lib/Events.ts b/apps/meteor/server/sdk/lib/Events.ts index a5817798e8ab..2d0c9d686d8b 100644 --- a/apps/meteor/server/sdk/lib/Events.ts +++ b/apps/meteor/server/sdk/lib/Events.ts @@ -23,6 +23,7 @@ import { AutoUpdateRecord } from '../types/IMeteor'; import { IInvite } from '../../../definition/IInvite'; import { IWebdavAccount } from '../../../definition/IWebdavAccount'; import { ICustomSound } from '../../../definition/ICustomSound'; +import type { VoipEventDataSignature } from '../../../definition/voip/IVoipClientEvents'; type ClientAction = 'inserted' | 'updated' | 'removed' | 'changed'; @@ -121,11 +122,9 @@ export type EventSignatures = { diff?: undefined | Record; id: string; }): void; - 'queue.agentcalled'(userid: string, queuename: string, callerid: Record): void; - 'queue.agentconnected'(userid: string, queuename: string, queuedcalls: string, waittimeinqueue: string): void; - 'queue.callerjoined'(userid: string, queuename: string, callerid: Record, 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; id: string }): void; }; diff --git a/apps/meteor/server/services/omnichannel-voip/service.ts b/apps/meteor/server/services/omnichannel-voip/service.ts index 68b5840ffef1..e68db91295a4 100644 --- a/apps/meteor/server/services/omnichannel-voip/service.ts +++ b/apps/meteor/server/services/omnichannel-voip/service.ts @@ -18,7 +18,7 @@ import { VoipClientEvents } from '../../../definition/voip/VoipClientEvents'; import { PaginatedResult } from '../../../definition/rest/helpers/PaginatedResult'; import { FindVoipRoomsParams } from './internalTypes'; import { ILivechatAgent } from '../../../definition/ILivechatAgent'; -import { Notifications } from '../../../app/notifications/server'; +import { api } from '../../sdk/api'; export class OmnichannelVoipService extends ServiceClassInternal implements IOmnichannelVoipService { protected name = 'omnichannel-voip'; @@ -70,8 +70,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 { diff --git a/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts b/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts index a26290e1695a..b27df026fd45 100644 --- a/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts +++ b/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts @@ -89,8 +89,8 @@ export class ContinuousMonitor extends Command { async processQueueMembershipChange(event: IQueueMemberAdded | IQueueMemberRemoved): Promise { 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: 'queuememberadded' }); } else if (isIQueueMemberRemovedEvent(event)) { - api.broadcast(`queue.queuememberremoved`, user._id, queueName, calls); + api.broadcast(`voip.events`, user._id, { event: 'queuememberremoved', 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: 'agentcalled', data: { callerId, queue: event.queue } }); + // api.broadcast('queue.agentcalled', user._id, event.queue, callerId); } async storePbxEvent(event: IQueueEvent | IContactStatus, eventName: string): Promise { @@ -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: 'callerjoined', 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: 'callabandoned', 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: 'agentconnected', + data: { queue: event.queue, queuedCalls: calls, waitTimeInQueue: event.holdtime }, + }); }); break; } From 2d4bb2c773f08da00b304371a45b7a7322976890 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 11 Apr 2022 09:15:56 -0600 Subject: [PATCH 2/4] Fix typings --- .../providers/CallProvider/CallProvider.tsx | 81 ++++++++++--------- .../definition/voip/IVoipClientEvents.ts | 7 ++ 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 98797b874f54..2825c43379d4 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -8,7 +8,7 @@ import { getUserPreference } from '../../../app/utils/client'; import { IVoipRoom } from '../../../definition/IRoom'; import { IUser } from '../../../definition/IUser'; import { ICallerInfo } from '../../../definition/voip/ICallerInfo'; -import { VoipEventDataSignature } from '../../../definition/voip/IVoipClientEvents'; +import { VoipEventDataSignature, isVoipEventAgentCalled, isVoipEventAgentConnected, isVoipEventCallerJoined, isVoipEventQueueMemberAdded, isVoipEventQueueMemberRemoved, isVoipEventCallAbandoned } from '../../../definition/voip/IVoipClientEvents'; import { WrapUpCallModal } from '../../components/voip/modal/WrapUpCallModal'; import { CallContext, CallContextValue } from '../../contexts/CallContext'; import { useSetModal } from '../../contexts/ModalContext'; @@ -68,45 +68,48 @@ export const CallProvider: FC = ({ children }) => { return; } - const handleEventReceived = async ({ event, data }: VoipEventDataSignature): Promise => { - switch (event) { - case 'agentcalled': { - queueAggregator.callRinging({ queuename: data.queue, callerid: data.callerId }); - setQueueName(queueAggregator.getCurrentQueueName()); - break; - } - case 'agentconnected': { - queueAggregator.callPickedup({ queuename: data.queue, queuedcalls: data.queuedCalls, waittimeinqueue: data.waitTimeInQueue }); - setQueueName(queueAggregator.getCurrentQueueName()); - setQueueCounter(queueAggregator.getCallWaitingCount()); - break; - } - case 'callerjoined': { - queueAggregator.queueJoined({ queuename: data.queue, callerid: data.callerId, queuedcalls: data.queuedCalls }); - setQueueCounter(queueAggregator.getCallWaitingCount()); - break; - } - case 'queuememberadded': { - queueAggregator.memberAdded({ queuename: data.queue, queuedcalls: data.queuedCalls }); - setQueueName(queueAggregator.getCurrentQueueName()); - setQueueCounter(queueAggregator.getCallWaitingCount()); - break; - } - case 'queuememberremoved': { - queueAggregator.memberRemoved({ queuename: data.queue, queuedcalls: data.queuedCalls }); - setQueueCounter(queueAggregator.getCallWaitingCount()); - break; - } - case 'callabandoned': { - queueAggregator.queueAbandoned({ queuename: data.queue, queuedcallafterabandon: data.queuedCallAfterAbandon }); - setQueueName(queueAggregator.getCurrentQueueName()); - setQueueCounter(queueAggregator.getCallWaitingCount()); - break; - } - default: { - console.warn('Unknown event received', event); - } + const handleEventReceived = async (event: VoipEventDataSignature): Promise => { + 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}/voip.events`, handleEventReceived); diff --git a/apps/meteor/definition/voip/IVoipClientEvents.ts b/apps/meteor/definition/voip/IVoipClientEvents.ts index fa4a04443d0b..21c013b77bfe 100644 --- a/apps/meteor/definition/voip/IVoipClientEvents.ts +++ b/apps/meteor/definition/voip/IVoipClientEvents.ts @@ -31,3 +31,10 @@ export type VoipEventDataSignature = event: 'callabandoned'; data: { queuedCallAfterAbandon: string; queue: string }; }; + +export const isVoipEventAgentCalled = (data: VoipEventDataSignature): data is { event: 'agentcalled'; data: { callerId: { id: string; name: string }; queue: string } } => data.event === 'agentcalled'; +export const isVoipEventAgentConnected = (data: VoipEventDataSignature): data is { event: 'agentconnected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } => data.event === 'agentconnected'; +export const isVoipEventCallerJoined = (data: VoipEventDataSignature): data is { event: 'callerjoined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string } } => data.event === 'callerjoined'; +export const isVoipEventQueueMemberAdded = (data: VoipEventDataSignature): data is { event: 'queuememberadded'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberadded'; +export const isVoipEventQueueMemberRemoved = (data: VoipEventDataSignature): data is { event: 'queuememberremoved'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberremoved'; +export const isVoipEventCallAbandoned = (data: VoipEventDataSignature): data is { event: 'callabandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'callabandoned'; From eac7027d9463cb4751e4fd1fe311c0d9b78e6ec7 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 11 Apr 2022 09:21:50 -0600 Subject: [PATCH 3/4] Fix lintin --- .../providers/CallProvider/CallProvider.tsx | 10 ++++++- .../definition/voip/IVoipClientEvents.ts | 26 ++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 2825c43379d4..78b61a451dfd 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -8,7 +8,15 @@ import { getUserPreference } from '../../../app/utils/client'; import { IVoipRoom } from '../../../definition/IRoom'; import { IUser } from '../../../definition/IUser'; import { ICallerInfo } from '../../../definition/voip/ICallerInfo'; -import { VoipEventDataSignature, isVoipEventAgentCalled, isVoipEventAgentConnected, isVoipEventCallerJoined, isVoipEventQueueMemberAdded, isVoipEventQueueMemberRemoved, isVoipEventCallAbandoned } from '../../../definition/voip/IVoipClientEvents'; +import { + VoipEventDataSignature, + isVoipEventAgentCalled, + isVoipEventAgentConnected, + isVoipEventCallerJoined, + isVoipEventQueueMemberAdded, + isVoipEventQueueMemberRemoved, + isVoipEventCallAbandoned, +} from '../../../definition/voip/IVoipClientEvents'; import { WrapUpCallModal } from '../../components/voip/modal/WrapUpCallModal'; import { CallContext, CallContextValue } from '../../contexts/CallContext'; import { useSetModal } from '../../contexts/ModalContext'; diff --git a/apps/meteor/definition/voip/IVoipClientEvents.ts b/apps/meteor/definition/voip/IVoipClientEvents.ts index 21c013b77bfe..9e933ad529a7 100644 --- a/apps/meteor/definition/voip/IVoipClientEvents.ts +++ b/apps/meteor/definition/voip/IVoipClientEvents.ts @@ -32,9 +32,23 @@ export type VoipEventDataSignature = data: { queuedCallAfterAbandon: string; queue: string }; }; -export const isVoipEventAgentCalled = (data: VoipEventDataSignature): data is { event: 'agentcalled'; data: { callerId: { id: string; name: string }; queue: string } } => data.event === 'agentcalled'; -export const isVoipEventAgentConnected = (data: VoipEventDataSignature): data is { event: 'agentconnected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } => data.event === 'agentconnected'; -export const isVoipEventCallerJoined = (data: VoipEventDataSignature): data is { event: 'callerjoined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string } } => data.event === 'callerjoined'; -export const isVoipEventQueueMemberAdded = (data: VoipEventDataSignature): data is { event: 'queuememberadded'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberadded'; -export const isVoipEventQueueMemberRemoved = (data: VoipEventDataSignature): data is { event: 'queuememberremoved'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberremoved'; -export const isVoipEventCallAbandoned = (data: VoipEventDataSignature): data is { event: 'callabandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'callabandoned'; +export const isVoipEventAgentCalled = ( + data: VoipEventDataSignature, +): data is { event: 'agentcalled'; data: { callerId: { id: string; name: string }; queue: string } } => data.event === 'agentcalled'; +export const isVoipEventAgentConnected = ( + data: VoipEventDataSignature, +): data is { event: 'agentconnected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } => + data.event === 'agentconnected'; +export const isVoipEventCallerJoined = ( + data: VoipEventDataSignature, +): data is { event: 'callerjoined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string } } => + data.event === 'callerjoined'; +export const isVoipEventQueueMemberAdded = ( + data: VoipEventDataSignature, +): data is { event: 'queuememberadded'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberadded'; +export const isVoipEventQueueMemberRemoved = ( + data: VoipEventDataSignature, +): data is { event: 'queuememberremoved'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberremoved'; +export const isVoipEventCallAbandoned = ( + data: VoipEventDataSignature, +): data is { event: 'callabandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'callabandoned'; From c5640f4e8e793249891a4812a2591a1dc069900d Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 19 Apr 2022 13:56:16 -0600 Subject: [PATCH 4/4] change names --- .../asterisk/ami/ContinuousMonitor.ts | 12 ++++---- .../src/voip/IVoipClientEvents.ts | 28 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts b/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts index 8a5260791dd3..92e84715746c 100644 --- a/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts +++ b/apps/meteor/server/services/voip/connector/asterisk/ami/ContinuousMonitor.ts @@ -101,9 +101,9 @@ export class ContinuousMonitor extends Command { }); if (user) { if (isIQueueMemberAddedEvent(event)) { - api.broadcast(`voip.events`, user._id, { data: { queue, queuedCalls: calls }, event: 'queuememberadded' }); + api.broadcast(`voip.events`, user._id, { data: { queue, queuedCalls: calls }, event: 'queue-member-added' }); } else if (isIQueueMemberRemovedEvent(event)) { - api.broadcast(`voip.events`, user._id, { event: 'queuememberremoved', data: { queue, queuedCalls: calls } }); + api.broadcast(`voip.events`, user._id, { event: 'queue-member-removed', data: { queue, queuedCalls: calls } }); } } } @@ -130,7 +130,7 @@ export class ContinuousMonitor extends Command { name: event.calleridname, }; - api.broadcast('voip.events', user._id, { event: 'agentcalled', data: { callerId, queue: event.queue } }); + api.broadcast('voip.events', user._id, { event: 'agent-called', data: { callerId, queue: event.queue } }); // api.broadcast('queue.agentcalled', user._id, event.queue, callerId); } @@ -186,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('voip.events', m, { event: 'callerjoined', data: { callerId, queue: event.queue, queuedCalls: event.count } }); + api.broadcast('voip.events', m, { event: 'caller-joined', data: { callerId, queue: event.queue, queuedCalls: event.count } }); }); break; } @@ -195,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('voip.events', m, { event: 'callabandoned', data: { queue: event.queue, queuedCallAfterAbandon: calls } }); + api.broadcast('voip.events', m, { event: 'call-abandoned', data: { queue: event.queue, queuedCallAfterAbandon: calls } }); }); break; } @@ -207,7 +207,7 @@ export class ContinuousMonitor extends Command { members.forEach((m) => { // event.holdtime signifies wait time in the queue. api.broadcast('voip.events', m, { - event: 'agentconnected', + event: 'agent-connected', data: { queue: event.queue, queuedCalls: calls, waitTimeInQueue: event.holdtime }, }); }); diff --git a/packages/core-typings/src/voip/IVoipClientEvents.ts b/packages/core-typings/src/voip/IVoipClientEvents.ts index 9e933ad529a7..70bd910e3d76 100644 --- a/packages/core-typings/src/voip/IVoipClientEvents.ts +++ b/packages/core-typings/src/voip/IVoipClientEvents.ts @@ -8,47 +8,47 @@ export type VoipPropagatedEvents = export type VoipEventDataSignature = | { - event: 'agentcalled'; + event: 'agent-called'; data: { callerId: { id: string; name: string }; queue: string }; } | { - event: 'agentconnected'; + event: 'agent-connected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string }; } | { - event: 'callerjoined'; + event: 'caller-joined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string }; } | { - event: 'queuememberadded'; + event: 'queue-member-added'; data: { queue: string; queuedCalls: string }; } | { - event: 'queuememberremoved'; + event: 'queue-member-removed'; data: { queue: string; queuedCalls: string }; } | { - event: 'callabandoned'; + event: 'call-abandoned'; data: { queuedCallAfterAbandon: string; queue: string }; }; export const isVoipEventAgentCalled = ( data: VoipEventDataSignature, -): data is { event: 'agentcalled'; data: { callerId: { id: string; name: string }; queue: string } } => data.event === 'agentcalled'; +): 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: 'agentconnected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } => - data.event === 'agentconnected'; +): data is { event: 'agent-connected'; data: { queue: string; queuedCalls: string; waitTimeInQueue: string } } => + data.event === 'agent-connected'; export const isVoipEventCallerJoined = ( data: VoipEventDataSignature, -): data is { event: 'callerjoined'; data: { callerId: { id: string; name: string }; queue: string; queuedCalls: string } } => - data.event === 'callerjoined'; +): 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: 'queuememberadded'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberadded'; +): data is { event: 'queue-member-added'; data: { queue: string; queuedCalls: string } } => data.event === 'queue-member-added'; export const isVoipEventQueueMemberRemoved = ( data: VoipEventDataSignature, -): data is { event: 'queuememberremoved'; data: { queue: string; queuedCalls: string } } => data.event === 'queuememberremoved'; +): data is { event: 'queue-member-removed'; data: { queue: string; queuedCalls: string } } => data.event === 'queue-member-removed'; export const isVoipEventCallAbandoned = ( data: VoipEventDataSignature, -): data is { event: 'callabandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'callabandoned'; +): data is { event: 'call-abandoned'; data: { queuedCallAfterAbandon: string; queue: string } } => data.event === 'call-abandoned';