Skip to content

Instantly share code, notes, and snippets.

@jmadden
Last active March 11, 2021 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmadden/f25d9375f0faea9ae6d5effd8e59ad14 to your computer and use it in GitHub Desktop.
Save jmadden/f25d9375f0faea9ae6d5effd8e59ad14 to your computer and use it in GitHub Desktop.
Location plugin-dual-channel-recording/src/listeners/CustomListeners.js
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