Last active
March 11, 2021 18:49
-
-
Save jmadden/f25d9375f0faea9ae6d5effd8e59ad14 to your computer and use it in GitHub Desktop.
Location plugin-dual-channel-recording/src/listeners/CustomListeners.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Actions, Manager, TaskHelper } from '@twilio/flex-ui'; | |
import { getCustomerParticipant, getMyParticipant } from '../helpers'; | |
const manager = Manager.getInstance(); | |
const startCallRecording = async (callSid, callbackParameters) => { | |
console.debug('Creating recording for call SID:', callSid); | |
const fetchUrl = `https://${process.env.REACT_APP_SERVERLESS_DOMAIN}/recording/create`; | |
const fetchBody = { | |
Token: manager.store.getState().flex.session.ssoTokenPayload.token, | |
callSid, | |
callbackParametersJson: callbackParameters && JSON.stringify(callbackParameters) | |
}; | |
const fetchOptions = { | |
method: 'POST', | |
body: new URLSearchParams(fetchBody), | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' | |
} | |
}; | |
const recordingResponse = await fetch(fetchUrl, fetchOptions); | |
const recording = await recordingResponse.json(); | |
console.debug('Created recording', recording); | |
return recording; | |
} | |
const addRecordingToTask = async (task, recording) => { | |
const state = manager.store.getState(); | |
const flexState = state && state.flex; | |
const workerState = flexState && flexState.worker; | |
const accountSid = workerState && workerState.source.accountSid; | |
const { sid: recordingSid, startTime: recordingStartTime } = recording; | |
const twilioApiBase = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}`; | |
const recordingUrl = `${twilioApiBase}/Recordings/${recordingSid}`; | |
const { attributes } = task; | |
const conversations = attributes.conversations || {}; | |
const media = conversations.media || []; | |
const waitTimeMs = 100; | |
const maxWaitTimeMs = 5000; | |
const newAttributes = { | |
...attributes, | |
conversations: { | |
...conversations, | |
media: [ | |
...media, | |
{ | |
url: recordingUrl, | |
type: 'VoiceRecording', | |
start_time: recordingStartTime, | |
channels: ['customer', 'others'], | |
}, | |
], | |
}, | |
}; | |
let waitForSegmentsInterval = setInterval(async () => { | |
const { segments } = attributes; | |
if (segments === null) { | |
return; | |
} | |
console.debug('Integration segments added to Task.'); | |
clearInterval(waitForSegmentsInterval); | |
waitForSegmentsInterval = undefined; | |
await task.setAttributes(newAttributes); | |
}, waitTimeMs); | |
setTimeout(() => { | |
if (waitForSegmentsInterval) { | |
console.debug( | |
`Integration Segements didn't show up within ${ | |
maxWaitTimeMs / 1000 | |
} seconds` | |
); | |
task.setAttributes(newAttributes); | |
clearInterval(waitForSegmentsInterval); | |
} | |
}, maxWaitTimeMs); | |
} | |
const isTaskActive = (task) => { | |
const { sid, taskStatus } = task; | |
if (taskStatus === 'canceled') { | |
return false; | |
} else { | |
return manager.workerClient.reservations.has(sid); | |
} | |
} | |
const waitForConferenceParticipants = (task) => { | |
const waitTimeMs = 100; | |
// For outbound calls, the customer participant doesn't join the conference | |
// until the called party answers. Need to allow enough time for that to happen. | |
const maxWaitTimeMs = 60000; | |
let waitForConferenceInterval = setInterval(async () => { | |
const { conference, taskStatus } = task; | |
console.debug('waitForConferenceInterval, taskStatus: ', taskStatus); | |
if (!isTaskActive(task)) { | |
console.debug('Call canceled, clearing waitForConferenceInterval'); | |
clearInterval(waitForConferenceInterval) | |
waitForConferenceInterval = undefined; | |
return; | |
} | |
const myParticipant = getMyParticipant(conference); | |
const customerParticipant = getCustomerParticipant(conference); | |
if (!myParticipant || !customerParticipant) { | |
return; | |
} | |
console.debug('Worker and customer participants joined conference'); | |
clearInterval(waitForConferenceInterval); | |
waitForConferenceInterval = undefined; | |
const { callSid: myCallSid, workerSid } = myParticipant; | |
const { callSid: customerCallSid } = customerParticipant; | |
const { attributes, sid: reservationSid, taskSid } = task; | |
const { conversations, direction, from, name, outbound_to } = attributes; | |
const conversationId = (conversations && conversations.conversation_id) || taskSid; | |
const callbackParameters = { | |
conversationId, | |
customerCallSid, | |
direction, | |
name, | |
number: outbound_to || from, | |
reservationSid, | |
taskSid, | |
type: 'call', | |
workerSid, | |
}; | |
const recording = await startCallRecording(myCallSid, callbackParameters); | |
await addRecordingToTask(task, recording); | |
}, waitTimeMs); | |
setTimeout(() => { | |
if (waitForConferenceInterval) { | |
console.debug(`Customer participant didn't show up within ${maxWaitTimeMs / 1000} seconds`); | |
clearInterval(waitForConferenceInterval) | |
} | |
}, maxWaitTimeMs); | |
} | |
const handleAcceptedCall = async (task) => { | |
waitForConferenceParticipants(task); | |
} | |
Actions.addListener('afterAcceptTask', async payload => { | |
const { task } = payload; | |
if (TaskHelper.isCallTask(task)) { | |
await handleAcceptedCall(task); | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment