Skip to content

Commit

Permalink
[SE-53] Add conditional-recording feature (#644)
Browse files Browse the repository at this point in the history
  • Loading branch information
dremin authored Sep 26, 2024
1 parent 617a1a0 commit 430387e
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/docs/feature-library/00_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ The **Flex Project Template** comes with a set of features enabled by default wi
| [Activity Skill Filter](activity-skill-filter) | _manage visibility for activities based on agent skills_ |
| [Branding](branding) | _customize the Flex interface for your brand_ |
| [Chat Transfer](chat-transfer) | _introduce programmable chat transfer functionality for agents_ |
| [Conditional Recording](conditional-recording) | _prevent recording certain calls based on task attributes or queue when using the native recording functionality_ |
| [Custom Hold Music](custom-hold-music) | _customize the experience when an agent places a call on hold_ |
| [Device Manager](device-manager) | _provide agents the ability to select the audio output device_ |
| [Dual Channel Recording](dual-channel-recording) | _automatically record both inbound and outbound calls in dual channel_ |
Expand Down
31 changes: 31 additions & 0 deletions docs/docs/feature-library/conditional-recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
sidebar_label: conditional-recording
title: conditional-recording
---

Flex includes a built-in call recording feature which can be enabled via the Twilio Console > Flex > Manage > Voice. This records the conference for every call handled within Flex, in either single- or dual-channel.

However, for regulatory compliance purposes, some implementations may need to prevent recording certain calls. This feature adds that capability to the built-in call recording feature, by preventing recording based on task queue or based on the presence of certain task attributes. The task attributes and/or task queues that should be excluded from recording are configurable.

:::info dual-channel-recording feature
The `conditional-recording` feature works with the native call recording functionality. It is not applicable in conjunction with [the template's `dual-channel-recording` feature](/feature-library/dual-channel-recording), which has its own conditional recording functionality.
:::

## setup and dependencies

If you are enabling the conditional recording feature, you must also **enable** the call recording flag within Twilio Console > Flex > Manage > Voice, otherwise recordings will not be accessible via Flex Insights.

The `conditional-recording` feature has the following settings:
- `enabled` - Set to `true` to enable the feature
- `exclude_attributes` - To exclude recording tasks based on the task attributes present, set this to an array of key/value pair objects. For example, to prevent recording outbound calls:
```
"exclude_attributes": [{ "key":"direction", "value":"outbound" }]
```
- `exclude_queues` - To exclude recording tasks based on queue name or queue SID, set this to an array of queue names or SIDs. For example:
```
"exclude_queues": ["Queue Name 1", "Queue Name 2"] // or ["WQxxx", "WQxxx2"]
```

## how it works

Before an inbound or outbound call task is accepted, the task is evaluated based on the defined attributes and/or queues to exclude from recording. The `payload.conferenceOptions.conferenceRecord` flag is set to `true` or `false` depending on the outcome of this evaluation. If this flag is set to `true`, then TaskRouter will initiate the conference recording immediately upon conference start.
7 changes: 5 additions & 2 deletions docs/docs/feature-library/dual-channel-recording.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: dual-channel-recording
:::info Native feature available
Native dual-channel recording is now available and can be enabled via the Twilio Console. The first agent to join the call will be on the left channel, and the other participants on the right channel. See [the changelog entry](https://www.twilio.com/en-us/changelog/dual-channel-voice-conference-recordings) for more details, including restrictions and instructions to enable.

This template feature will remain available for use cases that are not supported by the native feature.
This template feature will remain available for use cases that are not supported by the native feature. If you need the conditional recording functionality that this feature provides, you can use [the `conditional-recording` feature](/feature-library/conditional-recording) instead, which works with the native recording functionality.
:::

There are various ways to enable call recordings with Twilio Flex. Let's outline those methods to better understand when using this custom solution would be preferable.
Expand Down Expand Up @@ -49,7 +49,10 @@ You may also optionally specify task attributes and/or queues that should exclud
```
"exclude_attributes": [{ "key":"direction", "value":"outbound" }]
```
- To exclude recording tasks based on queue name or queue SID, set the `exclude_queues` configuration property to an array or queue names or SIDs.
- To exclude recording tasks based on queue name or queue SID, set the `exclude_queues` configuration property to an array of queue names or SIDs. For example:
```
"exclude_queues": ["Queue Name 1", "Queue Name 2"] // or ["WQxxx", "WQxxx2"]
```

## how it works

Expand Down
19 changes: 16 additions & 3 deletions flex-config/ui_attributes.common.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
"common": {
"log_level": "info",
"audit_log_ttl": 1209600,
"teams": ["Blue Team", "Red Team", "Gold Team", "VIP Team"],
"teams": [
"Blue Team",
"Red Team",
"Gold Team",
"VIP Team"
],
"departments": [
"General Management",
"Marketing",
Expand Down Expand Up @@ -235,7 +240,10 @@
"per_queue": {
"exampleQueueName": {
"require_disposition": true,
"dispositions": ["Promotional Sale", "Renewal"],
"dispositions": [
"Promotional Sale",
"Renewal"
],
"text_attributes": [],
"select_attributes": []
}
Expand Down Expand Up @@ -412,7 +420,12 @@
"force_conference_region": {
"enabled": false,
"region": ""
},
"conditional_recording": {
"enabled": false,
"exclude_attributes": [],
"exclude_queues": [],
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getFeatureFlags, getLoadedFeatures } from '../../utils/configuration';
import ConditionalRecordingConfig from './types/ServiceConfiguration';

const {
enabled = false,
exclude_attributes = [],
exclude_queues = [],
} = (getFeatureFlags()?.features?.conditional_recording as ConditionalRecordingConfig) || {};

export const isFeatureEnabled = () => {
return enabled;
};

export const getExcludedAttributes = () => {
return exclude_attributes;
};

export const getExcludedQueues = () => {
return exclude_queues;
};

export const isDualChannelEnabled = () => {
return getLoadedFeatures().includes('dual-channel-recording');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as Flex from '@twilio/flex-ui';

import { FlexActionEvent, FlexAction } from '../../../../types/feature-loader';
import logger from '../../../../utils/logger';
import { canRecordTask } from '../../helpers/conditionalRecordingHelper';
import { isDualChannelEnabled } from '../../config';

export const actionEvent = FlexActionEvent.before;
export const actionName = FlexAction.AcceptTask;
export const actionHook = function setRecordingFlag(flex: typeof Flex) {
flex.Actions.addListener(`${actionEvent}${actionName}`, async (payload) => {
if (!payload.task && !payload.sid) return;

// If the dual-channel-recording feature is enabled, it will perform the recording instead
if (isDualChannelEnabled()) {
logger.warn(
'[conditional-recording] Because the dual-channel-recording feature is enabled, the conditional-recording configuration will be ignored.',
);
return;
}

let task = payload.task;

if (!task) {
task = Flex.TaskHelper.getTaskByTaskSid(payload.sid);
}

payload.conferenceOptions.conferenceRecord = canRecordTask(task);
logger.debug(
`[conditional-recording] Recording flag for ${task.sid}: ${payload.conferenceOptions.conferenceRecord}`,
);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ITask } from '@twilio/flex-ui';

import { getExcludedAttributes, getExcludedQueues } from '../config';

export const canRecordTask = (task: ITask): boolean => {
if (getExcludedQueues().findIndex((queue) => queue === task.queueName || queue === task.queueSid) >= 0) {
return false;
}

for (const attribute of getExcludedAttributes()) {
if (task.attributes[attribute.key] === attribute.value) {
return false;
}
}

return true;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FeatureDefinition } from '../../types/feature-loader';
import { isFeatureEnabled } from './config';
// @ts-ignore
import hooks from './flex-hooks/**/*.*';

export const register = (): FeatureDefinition => {
if (!isFeatureEnabled()) return {};
return { name: 'conditional-recording', hooks: typeof hooks === 'undefined' ? [] : hooks };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface AttributesQualificationConfig {
key: string;
value: string;
}

export default interface ConditionalRecordingConfig {
enabled: boolean;
exclude_attributes: Array<AttributesQualificationConfig>;
exclude_queues: Array<string>;
}

0 comments on commit 430387e

Please sign in to comment.