From 11b3a8eef290fc12cb7ca193ed8e142fce065eb9 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Tue, 4 Jul 2023 15:34:59 -0300 Subject: [PATCH 1/5] Added conference/getParticipant function (#485) --- functions/conference/getParticipant.ts | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 functions/conference/getParticipant.ts diff --git a/functions/conference/getParticipant.ts b/functions/conference/getParticipant.ts new file mode 100644 index 00000000..2f60bbe7 --- /dev/null +++ b/functions/conference/getParticipant.ts @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import '@twilio-labs/serverless-runtime-types'; +import { Context, ServerlessCallback } from '@twilio-labs/serverless-runtime-types/types'; +import { + responseWithCors, + bindResolve, + error400, + error500, + functionValidator as TokenValidator, + success, +} from '@tech-matters/serverless-helpers'; + +type EnvVars = { + TWILIO_WORKSPACE_SID: string; +}; + +export type Body = { + callSid: string; + conferenceSid: string; + request: { cookies: {}; headers: {} }; +}; + +export const handler = TokenValidator( + async (context: Context, event: Body, callback: ServerlessCallback) => { + const response = responseWithCors(); + const resolve = bindResolve(callback)(response); + + const { callSid, conferenceSid } = event; + + try { + if (!callSid) return resolve(error400('callSid')); + if (!conferenceSid) return resolve(error400('conferenceSid')); + + const participant = await context + .getTwilioClient() + .conferences(conferenceSid) + .participants(callSid) + .fetch(); + + return resolve(success({ participant })); + } catch (err: any) { + return resolve(error500(err)); + } + }, +); From bf06ceb9ca169c04ff848e758fc3238093a1ce85 Mon Sep 17 00:00:00 2001 From: Stephen Okpalaononuju Date: Wed, 5 Jul 2023 10:32:37 +0100 Subject: [PATCH 2/5] test: log isVoiceTransferTimedOut params --- .../transfersListener.private.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/functions/taskrouterListeners/transfersListener.private.ts b/functions/taskrouterListeners/transfersListener.private.ts index ced3cd85..b4d78c6d 100644 --- a/functions/taskrouterListeners/transfersListener.private.ts +++ b/functions/taskrouterListeners/transfersListener.private.ts @@ -46,7 +46,7 @@ type EnvVars = { export type TransferMeta = { mode: 'COLD' | 'WARM'; - transferStatus: 'transferring' | 'accepted' | 'rejected'; + transferStatus: 'transferring' | 'accepted' | 'rejected' | 'timeout'; sidWithTaskControl: string; }; @@ -113,6 +113,22 @@ const isVoiceTransferOriginalInWrapup = ( taskAttributes.transferMeta && taskAttributes.transferMeta.transferStatus === 'accepted'; +const isVoiceTransferTimedOut = ( + eventType: EventType, + taskChannelUniqueName: string, + taskAttributes: { transferMeta?: TransferMeta }, +) => + // eventType === RESERVATION_TIMEOUT && + // taskChannelUniqueName === 'voice' && + // taskAttributes.transferMeta && + // taskAttributes.transferMeta.transferStatus === 'timeout' + console.log( + 'taskAttributes.transferMeta.transferStatus', + taskAttributes?.transferMeta?.transferStatus, + eventType, + taskChannelUniqueName, + ); + /** * Checks the event type to determine if the listener should handle the event or not. * If it returns true, the taskrouter will invoke this listener. @@ -132,6 +148,8 @@ export const handleEvent = async (context: Context, event: EventFields) const taskAttributes = JSON.parse(taskAttributesString); + console.log(isVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)); + /** * If a chat transfer gets accepted, it should: * 1) Complete the original task From 5ca5d1fa202a882a657df87b1e5434bc5b2a77e2 Mon Sep 17 00:00:00 2001 From: Stephen Okpalaononuju Date: Wed, 5 Jul 2023 14:18:45 +0100 Subject: [PATCH 3/5] ch: add checks for transfer timeout --- .../transfersListener.private.ts | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/functions/taskrouterListeners/transfersListener.private.ts b/functions/taskrouterListeners/transfersListener.private.ts index b4d78c6d..eb323f84 100644 --- a/functions/taskrouterListeners/transfersListener.private.ts +++ b/functions/taskrouterListeners/transfersListener.private.ts @@ -46,7 +46,7 @@ type EnvVars = { export type TransferMeta = { mode: 'COLD' | 'WARM'; - transferStatus: 'transferring' | 'accepted' | 'rejected' | 'timeout'; + transferStatus: 'transferring' | 'accepted' | 'rejected'; sidWithTaskControl: string; }; @@ -113,22 +113,15 @@ const isVoiceTransferOriginalInWrapup = ( taskAttributes.transferMeta && taskAttributes.transferMeta.transferStatus === 'accepted'; -const isVoiceTransferTimedOut = ( +const isWarmVoiceTransferTimedOut = ( eventType: EventType, taskChannelUniqueName: string, taskAttributes: { transferMeta?: TransferMeta }, ) => - // eventType === RESERVATION_TIMEOUT && - // taskChannelUniqueName === 'voice' && - // taskAttributes.transferMeta && - // taskAttributes.transferMeta.transferStatus === 'timeout' - console.log( - 'taskAttributes.transferMeta.transferStatus', - taskAttributes?.transferMeta?.transferStatus, - eventType, - taskChannelUniqueName, - ); - + eventType === RESERVATION_TIMEOUT && + taskChannelUniqueName === 'voice' && + taskAttributes.transferMeta && + taskAttributes.transferMeta.mode === 'WARM'; /** * Checks the event type to determine if the listener should handle the event or not. * If it returns true, the taskrouter will invoke this listener. @@ -148,7 +141,7 @@ export const handleEvent = async (context: Context, event: EventFields) const taskAttributes = JSON.parse(taskAttributesString); - console.log(isVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)); + console.log(isWarmVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)); /** * If a chat transfer gets accepted, it should: @@ -274,6 +267,29 @@ export const handleEvent = async (context: Context, event: EventFields) return; } + if (isWarmVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)) { + console.log('Handling warm voice transfer timeout...'); + + const client = context.getTwilioClient(); + + const updatedAttributes = { + ...taskAttributes, + transferMeta: { + ...taskAttributes.transferMeta, + sidWithTaskControl: taskAttributes.transferMeta.originalReservation, + transferStatus: 'timeout', + }, + }; + + await client.taskrouter + .workspaces(context.TWILIO_WORKSPACE_SID) + .tasks(taskSid) + .update({ attributes: JSON.stringify(updatedAttributes) }); + + console.log('Finished handling warm voice transfer timeout.'); + return; + } + /** * I'm not sure why Twilio is keeping the originalReservation in wrapup state * after a voice COLD transfer. This clause here completes this reservation. From 90e6f69a6529c1426e84280061ca9a93d94459de Mon Sep 17 00:00:00 2001 From: Stephen Okpalaononuju Date: Wed, 5 Jul 2023 17:29:16 +0100 Subject: [PATCH 4/5] implement updateWarmVoiceTransferAttributes for DRY purpose --- .../transfersListener.private.ts | 86 +++++++++---------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/functions/taskrouterListeners/transfersListener.private.ts b/functions/taskrouterListeners/transfersListener.private.ts index eb323f84..0b1344d3 100644 --- a/functions/taskrouterListeners/transfersListener.private.ts +++ b/functions/taskrouterListeners/transfersListener.private.ts @@ -122,6 +122,44 @@ const isWarmVoiceTransferTimedOut = ( taskChannelUniqueName === 'voice' && taskAttributes.transferMeta && taskAttributes.transferMeta.mode === 'WARM'; + +/** + * updateWarmVoiceTransferAttributes is a DRY function that checks + * when warm voice transfer gets rejected or timeout + * + * If a warm voice transfer gets rejected, it should: + * 1) Adjust original task attributes: + * - transferMeta.transferStatus: 'rejected' + * - transferMeta.sidWithTaskControl: to original reservation + * Same applies to when transfer timeout + */ +const updateWarmVoiceTransferAttributes = async ( + transferStatus: string, + context: Context, + taskAttributes: { transferMeta: { originalReservation: string } }, + taskSid: string, +) => { + console.log(`Handling warm voice transfer ${transferStatus}...`); + + const client = context.getTwilioClient(); + + const updatedAttributes = { + ...taskAttributes, + transferMeta: { + ...taskAttributes.transferMeta, + sidWithTaskControl: taskAttributes.transferMeta.originalReservation, + transferStatus, + }, + }; + + await client.taskrouter + .workspaces(context.TWILIO_WORKSPACE_SID) + .tasks(taskSid) + .update({ attributes: JSON.stringify(updatedAttributes) }); + + console.log(`Finished handling warm voice transfer ${transferStatus}.`); +}; + /** * Checks the event type to determine if the listener should handle the event or not. * If it returns true, the taskrouter will invoke this listener. @@ -236,57 +274,13 @@ export const handleEvent = async (context: Context, event: EventFields) return; } - /** - * If a warm voice transfer gets rejected, it should: - * 1) Adjust original task attributes: - * - transferMeta.transferStatus: 'rejected' - * - transferMeta.sidWithTaskControl: to original reservation - */ if (isWarmVoiceTransferRejected(eventType, taskChannelUniqueName, taskAttributes)) { - console.log('Handling warm voice transfer rejected...'); - - const client = context.getTwilioClient(); - - const updatedAttributes = { - ...taskAttributes, - transferMeta: { - ...taskAttributes.transferMeta, - sidWithTaskControl: taskAttributes.transferMeta.originalReservation, - transferStatus: 'rejected', - }, - }; - - await client.taskrouter - .workspaces(context.TWILIO_WORKSPACE_SID) - .tasks(taskSid) - .update({ - attributes: JSON.stringify(updatedAttributes), - }); - - console.log('Finished handling warm voice transfer rejected.'); + await updateWarmVoiceTransferAttributes('rejected', context, taskAttributes, taskSid); return; } if (isWarmVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)) { - console.log('Handling warm voice transfer timeout...'); - - const client = context.getTwilioClient(); - - const updatedAttributes = { - ...taskAttributes, - transferMeta: { - ...taskAttributes.transferMeta, - sidWithTaskControl: taskAttributes.transferMeta.originalReservation, - transferStatus: 'timeout', - }, - }; - - await client.taskrouter - .workspaces(context.TWILIO_WORKSPACE_SID) - .tasks(taskSid) - .update({ attributes: JSON.stringify(updatedAttributes) }); - - console.log('Finished handling warm voice transfer timeout.'); + await updateWarmVoiceTransferAttributes('timeout', context, taskAttributes, taskSid); return; } From abd5614f0c638e6014e0a3b6e0d8ad68f21d4288 Mon Sep 17 00:00:00 2001 From: Stephen Okpalaononuju Date: Fri, 7 Jul 2023 17:44:51 +0100 Subject: [PATCH 5/5] ch: implement feedback --- functions/taskrouterListeners/transfersListener.private.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/functions/taskrouterListeners/transfersListener.private.ts b/functions/taskrouterListeners/transfersListener.private.ts index 0b1344d3..1db8ccce 100644 --- a/functions/taskrouterListeners/transfersListener.private.ts +++ b/functions/taskrouterListeners/transfersListener.private.ts @@ -139,7 +139,7 @@ const updateWarmVoiceTransferAttributes = async ( taskAttributes: { transferMeta: { originalReservation: string } }, taskSid: string, ) => { - console.log(`Handling warm voice transfer ${transferStatus}...`); + console.info(`Handling warm voice transfer ${transferStatus} with taskSid ${taskSid}...`); const client = context.getTwilioClient(); @@ -157,7 +157,7 @@ const updateWarmVoiceTransferAttributes = async ( .tasks(taskSid) .update({ attributes: JSON.stringify(updatedAttributes) }); - console.log(`Finished handling warm voice transfer ${transferStatus}.`); + console.info(`Finished handling warm voice transfer ${transferStatus} with taskSid ${taskSid}.`); }; /** @@ -179,8 +179,6 @@ export const handleEvent = async (context: Context, event: EventFields) const taskAttributes = JSON.parse(taskAttributesString); - console.log(isWarmVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)); - /** * If a chat transfer gets accepted, it should: * 1) Complete the original task