diff --git a/functions/taskrouterListeners/janitorListener.private.ts b/functions/taskrouterListeners/janitorListener.private.ts index 4055c4a6..34c02da3 100644 --- a/functions/taskrouterListeners/janitorListener.private.ts +++ b/functions/taskrouterListeners/janitorListener.private.ts @@ -31,6 +31,7 @@ import { import type { ChatChannelJanitor } from '../helpers/chatChannelJanitor.private'; import type { ChannelToFlex } from '../helpers/customChannels/customChannelToFlex.private'; +import type { HasTaskControl, Attributes } from '../transfer/helpers.private'; export const eventTypes: EventType[] = [ TASK_CANCELED, @@ -47,7 +48,11 @@ type EnvVars = { const isCleanupPostSurvey = (eventType: EventType, taskAttributes: { isSurveyTask?: boolean }) => (eventType === TASK_CANCELED || eventType === TASK_WRAPUP) && taskAttributes.isSurveyTask; -const isCleanupCustomChannel = (eventType: EventType, taskAttributes: { channelType?: string }) => { +const handleTaskControl = Runtime.getFunctions()['transfer/helpers'].path; +const taskControl = require(handleTaskControl) as HasTaskControl; + +const isCleanupCustomChannel = (eventType: EventType, taskAttributes: Attributes) => { + console.log('hasTaskControl(taskAttributes) 1', taskControl.hasTaskControl(taskAttributes)); if ( !( eventType === TASK_DELETED || @@ -58,6 +63,10 @@ const isCleanupCustomChannel = (eventType: EventType, taskAttributes: { channelT return false; } + console.log('hasTaskControl(taskAttributes) 2', taskControl.hasTaskControl(taskAttributes)); + + if (!taskControl.hasTaskControl(taskAttributes)) return false; + const handlerPath = Runtime.getFunctions()['helpers/customChannels/customChannelToFlex'].path; const channelToFlex = require(handlerPath) as ChannelToFlex; diff --git a/functions/taskrouterListeners/transfersListener.private.ts b/functions/taskrouterListeners/transfersListener.private.ts index 1db8ccce..1bb92582 100644 --- a/functions/taskrouterListeners/transfersListener.private.ts +++ b/functions/taskrouterListeners/transfersListener.private.ts @@ -27,6 +27,7 @@ import { RESERVATION_REJECTED, RESERVATION_TIMEOUT, RESERVATION_WRAPUP, + // RESERVATION_CANCELED, TASK_CANCELED, TASK_QUEUE_ENTERED, } from '@tech-matters/serverless-helpers/taskrouter'; @@ -93,6 +94,15 @@ const isChatTransferToQueueComplete = ( isChatTransfer(taskChannelUniqueName, taskAttributes) && taskAttributes.transferTargetType === 'queue'; +// const isChatTransferToQueueWorker = ( +// eventType: EventType, +// taskChannelUniqueName: string, +// taskAttributes: ChatTransferTaskAttributes, +// ) => +// eventType === TASK_QUEUE_ENTERED && +// isChatTransfer(taskChannelUniqueName, taskAttributes) && +// taskAttributes.transferTargetType === 'worker'; + const isWarmVoiceTransferRejected = ( eventType: EventType, taskChannelUniqueName: string, @@ -167,18 +177,25 @@ const updateWarmVoiceTransferAttributes = async ( export const shouldHandle = (event: EventFields) => eventTypes.includes(event.EventType); export const handleEvent = async (context: Context, event: EventFields) => { + const { + EventType: eventType, + TaskChannelUniqueName: taskChannelUniqueName, + TaskSid: taskSid, + TaskAttributes: taskAttributesString, + } = event; + + const clients = context.getTwilioClient(); + const testClient = await clients.taskrouter + .workspaces(context.TWILIO_WORKSPACE_SID) + .tasks(taskSid) + .fetch(); try { - const { - EventType: eventType, - TaskChannelUniqueName: taskChannelUniqueName, - TaskSid: taskSid, - TaskAttributes: taskAttributesString, - } = event; - console.log(`===== Executing TransfersListener for event: ${eventType} =====`); const taskAttributes = JSON.parse(taskAttributesString); + console.log('testClient here', testClient.assignmentStatus, taskAttributes.isInMyBehalf); + /** * If a chat transfer gets accepted, it should: * 1) Complete the original task @@ -189,6 +206,15 @@ export const handleEvent = async (context: Context, event: EventFields) const { originalTask: originalTaskSid } = taskAttributes.transferMeta; const client = context.getTwilioClient(); + console.log('testClient here 1', testClient.assignmentStatus); + console.log('originalTaskSid 1:', originalTaskSid, 'taskSid: 1', taskSid); + + console.log( + 'isChatTransferToWorkerAccepted', + originalTaskSid, + taskAttributes.transferTargetType, + ); + await client.taskrouter .workspaces(context.TWILIO_WORKSPACE_SID) .tasks(originalTaskSid) @@ -211,6 +237,14 @@ export const handleEvent = async (context: Context, event: EventFields) const { originalTask: originalTaskSid } = taskAttributes.transferMeta; const client = context.getTwilioClient(); + console.log('testClient here 2', testClient.assignmentStatus); + console.log('originalTaskSid: 2', originalTaskSid, 'taskSid: 2', taskSid); + + console.log( + 'isChatTransferToQueueComplete', + originalTaskSid, + taskAttributes.transferTargetType, + ); await client.taskrouter .workspaces(context.TWILIO_WORKSPACE_SID) @@ -224,6 +258,30 @@ export const handleEvent = async (context: Context, event: EventFields) return; } + // if (isChatTransferToQueueWorker(eventType, taskChannelUniqueName, taskAttributes)) { + // console.log('Handling chat transfer to queue entering target queue...'); + + // const { originalTask: originalTaskSid } = taskAttributes.transferMeta; + // const client = context.getTwilioClient(); + + // console.log( + // 'isChatTransferToQueueComplete', + // originalTaskSid, + // taskAttributes.transferTargetType, + // ); + + // await client.taskrouter + // .workspaces(context.TWILIO_WORKSPACE_SID) + // .tasks(originalTaskSid) + // .update({ + // assignmentStatus: 'pending', + // reason: 'task transferred into queue', + // }); + + // console.log('Finished handling chat queue transfer.'); + // return; + // } + /** * If a chat transfer gets rejected, it should: * 1) Adjust original task attributes: @@ -237,6 +295,14 @@ export const handleEvent = async (context: Context, event: EventFields) const { originalTask: originalTaskSid } = taskAttributes.transferMeta; const client = context.getTwilioClient(); + console.log( + 'isChatTransferToWorkerRejected', + originalTaskSid, + taskAttributes.transferTargetType, + ); + console.log('testClient here 3', testClient.assignmentStatus); + console.log('originalTaskSid: 3', originalTaskSid, 'taskSid: 3', taskSid); + const originalTask = await client.taskrouter .workspaces(context.TWILIO_WORKSPACE_SID) .tasks(originalTaskSid) @@ -252,6 +318,7 @@ export const handleEvent = async (context: Context, event: EventFields) transferMeta: { ...originalAttributes.transferMeta, sidWithTaskControl: originalAttributes.transferMeta.originalReservation, + transferStatus: 'rejected', }, }; @@ -268,6 +335,9 @@ export const handleEvent = async (context: Context, event: EventFields) }), ]); + console.log('testClient here 4', testClient.assignmentStatus); + console.log('originalTaskSid: 4', originalTaskSid, 'taskSid: 4', taskSid); + console.log('Finished handling chat transfer rejected.'); return; } @@ -308,6 +378,7 @@ export const handleEvent = async (context: Context, event: EventFields) } catch (err) { console.log('===== TransfersListener has failed ====='); console.log(String(err)); + console.log('testClient here 5', testClient.assignmentStatus); throw err; } }; diff --git a/functions/transfer/helpers.private.ts b/functions/transfer/helpers.private.ts new file mode 100644 index 00000000..82a31e87 --- /dev/null +++ b/functions/transfer/helpers.private.ts @@ -0,0 +1,53 @@ +/** + * 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/. + */ + +/* eslint-disable global-require */ +/* eslint-disable import/no-dynamic-require */ + +export type TransferMeta = { + mode: 'COLD' | 'WARM'; + transferStatus: 'transferring' | 'accepted' | 'rejected'; + sidWithTaskControl: string; +}; + +export type Attributes = { + transferMeta?: TransferMeta; + isContactlessTask?: true; + isInMyBehalf?: true; + taskSid: string; + channelType?: string; +}; + +export const offlineContactTaskSid = 'offline-contact-task-sid'; + +export const isInMyBehalfITask = (task: Attributes) => + task && task.isContactlessTask && task.isInMyBehalf; + +export const isOfflineContactTask = (task: Attributes) => task.taskSid === offlineContactTaskSid; + +export const isTwilioTask = (task: Attributes) => + task && !isOfflineContactTask(task) && !isInMyBehalfITask(task); + +export const hasTransferStarted = (task: Attributes) => Boolean(task && task.transferMeta); + +export const hasTaskControl = (task: Attributes) => + !isTwilioTask(task) || + !hasTransferStarted(task) || + task.transferMeta?.sidWithTaskControl === task.taskSid; + +export type HasTaskControl = { + hasTaskControl: typeof hasTaskControl; +}; diff --git a/functions/webhooks/line/FlexToLine.protected.ts b/functions/webhooks/line/FlexToLine.protected.ts index b5a3922f..481f9ce7 100644 --- a/functions/webhooks/line/FlexToLine.protected.ts +++ b/functions/webhooks/line/FlexToLine.protected.ts @@ -122,6 +122,7 @@ export const handler = async ( switch (result.status) { case 'sent': resolve(success(result.response)); + console.log('result.response 1', result.response); return; case 'ignored': resolve(success('Ignored event.')); diff --git a/functions/webhooks/line/LineToFlex.ts b/functions/webhooks/line/LineToFlex.ts index 5f8ee328..1d97cea0 100644 --- a/functions/webhooks/line/LineToFlex.ts +++ b/functions/webhooks/line/LineToFlex.ts @@ -157,6 +157,7 @@ export const handler = async ( switch (result.status) { case 'sent': responses.push(result.response); + console.log('result.response 2', result.response, responses); break; case 'ignored': responses.push('Ignored event.');