Skip to content

Commit

Permalink
feat: added new DTMF endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
manchuck committed Jan 7, 2025
1 parent e668de0 commit 72f9472
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 6 deletions.
19 changes: 19 additions & 0 deletions packages/voice/__tests__/__dataSets__/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { callPhone } from '../common';

export default [
{
label: 'unsubscribe to DTMF events',
requests: [
[
`/v1/calls/${callPhone.uuid}/input/dtmf`,
'DELETE',
],
],
responses: [[200]],
clientMethod: 'unsubscribeDTMF',
parameters: [callPhone.uuid],
generator: false,
error: false,
expected: undefined,
},
];
5 changes: 5 additions & 0 deletions packages/voice/__tests__/__dataSets__/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import callTests from './calls';
import createTests from './create';
import updateTests from './update';
import deleteTests from './delete';

export default [
{
Expand All @@ -15,4 +16,8 @@ export default [
label: 'Update call',
tests: updateTests,
},
{
label: 'Delete call',
tests: deleteTests,
},
];
59 changes: 59 additions & 0 deletions packages/voice/__tests__/__dataSets__/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,47 @@ export default [
error: false,
expected: undefined,
},
{
label: 'transfer call with NCCO Input DTMF Action',
requests: [
[
`/v1/calls/${callPhone.uuid}`,
'PUT',
{
action: 'transfer',
destination: {
type: 'ncco',
ncco: [
{
action: NCCOActions.INPUT,
dtmf: {
digits: '1234',
},
mode: 'asynchronous',
},
],
},
},
],
],
responses: [[204]],
clientMethod: 'transferCallWithNCCO',
parameters: [
callPhone.uuid,
[
{
action: NCCOActions.INPUT,
dtmf: {
digits: '1234',
},
mode: 'asynchronous',
},
],
],
generator: false,
error: false,
expected: undefined,
},
{
label: 'hangup call',
requests: [
Expand Down Expand Up @@ -405,4 +446,22 @@ export default [
error: false,
expected: undefined,
},
{
label: 'subscribe to DTMF events',
requests: [
[
`/v1/calls/${callPhone.uuid}/input/dtmf`,
'PUT',
{
event_url: ['https://example.com/dtmf'],
},
],
],
responses: [[200]],
clientMethod: 'subscribeDTMF',
parameters: [callPhone.uuid, 'https://example.com/dtmf'],
generator: false,
error: false,
expected: undefined,
},
];
17 changes: 16 additions & 1 deletion packages/voice/lib/classes/NCCO/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Serializable } from '../../ncco';
/**
* Represents an Input action in the Nexmo Call Control Object (NCCO) for gathering user input.
*/
export class Input implements InputAction, Serializable {
export class Input implements Pick<InputAction, 'action' | 'type' | 'dtmf' | 'speech' | 'eventUrl' | 'eventMethod' | 'mode'>, Serializable {
/**
* The action type, which is always 'input'.
*/
Expand Down Expand Up @@ -38,6 +38,16 @@ export class Input implements InputAction, Serializable {
*/
eventMethod?: string;

/**
* Input processing mode, currently only applicable to DTMF. Valid values are
* synchronous (the default) and asynchronous. If set to asynchronous, all
* DTMF settings must be left blank. In asynchronous mode, digits are sent one
* at a time to the event webhook in real time. In the default synchronous
* mode, this is controlled by the DTMF settings instead and the inputs are
* sent in batch.
*/
mode?: 'asynchronous' | 'syncronous';

/**
* Create a new Input instance.
*
Expand All @@ -51,6 +61,7 @@ export class Input implements InputAction, Serializable {
speech?: SpeechSettings,
eventUrl?: string,
eventMethod?: string,
mode?: 'asynchronous' | 'synchronous',
) {
if (dtmf) {
this.type.push('dtmf');
Expand All @@ -70,6 +81,10 @@ export class Input implements InputAction, Serializable {
this.eventMethod = eventMethod;
}

if (mode) {
this.mode = mode;
}

if (this.type.length === 0) {
throw new TypeError(
'Input action must have at least either DTMF or Speech settings',
Expand Down
2 changes: 1 addition & 1 deletion packages/voice/lib/classes/NCCO/NCCOBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class NCCOBuilder {
public addAction(action: NCCOAction): NCCOBuilder {
this.actions.push(
('serializeToNCCO' in action
? (action as Serializable).serializeToNCCO()
? (action as unknown as Serializable).serializeToNCCO()
: action
) as NCCOAction,
);
Expand Down
12 changes: 11 additions & 1 deletion packages/voice/lib/types/NCCO/InputAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,14 @@ export type InputAction = {
* Valid values are 'GET' and 'POST'.
*/
eventMethod?: string;
};

/**
* Input processing mode, currently only applicable to DTMF. Valid values are
* synchronous (the default) and asynchronous. If set to asynchronous, all
* DTMF settings must be left blank. In asynchronous mode, digits are sent one
* at a time to the event webhook in real time. In the default synchronous
* mode, this is controlled by the DTMF settings instead and the inputs are
* sent in batch.
*/
mode?: 'asynchronous' | 'synchronous';
} | Record<string, unknown>;
49 changes: 46 additions & 3 deletions packages/voice/lib/voice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
OutboundCall,
CallWithNCCO,
SIPEndpoint,
CallEndpoint,
} from './types';

import { ResponseTypes } from '@vonage/vetch';
Expand All @@ -37,7 +38,7 @@ const NCCOToApiCalls = (ncco: Action[]): Array<Action> => ncco.map((action) => {
case NCCOActions.CONNECT:
return {
...action,
endpoint: action.endpoint.map((endpoint) => {
endpoint: (action.endpoint as Array<CallEndpoint>)?.map((endpoint) => {
switch (endpoint.type) {
case 'sip':
return {
Expand Down Expand Up @@ -331,6 +332,48 @@ export class Voice extends Client {
) as CallUpdateResult;
}

/**
* Register a listener to receive asynchronous DTMF inputs from a call
*
* This is only applicable to Input NCCO events with the mode set to
* asynchronous. The payload delivered to this URL will be an Input webhook
* event with a single DTMF digit every time the callee enters DTMF into the
* call.
*
* @param {string} uuid - The UUID of the call leg
* @param {string} eventUrl - The The URL to send DTMF events to, as a POST request.
* @return {Promise<CallUpdateResult>} A promise that resolves to the result
*
* @example
* ```ts
* const result = await voiceClient.subscribeDTMF('CALL_UUID', 'https://example.com/dtmf');
* console.log(result.status);
* ```
*/
async subscribeDTMF(uuid: string, eventUrl: string): Promise<void> {
await this.sendPutRequest<UpdateCallResponse>(
`${this.config.apiHost}/v1/calls/${uuid}/input/dtmf`,
{ event_url: [eventUrl]},
);
}

/**
* Removes the registered DTMF listener
* @param {string} uuid - The UUID of the call leg
* @return {Promise<CallUpdateResult>} A promise that resolves to the result
*
* @example
* ```ts
* const result = await voiceClient.subscribeDTMF('CALL_UUID', 'https://example.com/dtmf');
* console.log(result.status);
* ```
*/
async unsubscribeDTMF(uuid: string): Promise<void> {
await this.sendDeleteRequest<UpdateCallResponse>(
`${this.config.apiHost}/v1/calls/${uuid}/input/dtmf`,
);
}

/**
* Plays text-to-speech (TTS) audio on an active call.
*
Expand Down Expand Up @@ -582,7 +625,7 @@ export class Voice extends Client {
/**
* Download the recording of a call to the specified file path.
*
* @param {string} file - The name of the recording file to download.
* @param {string} file - The name or recording id of the recording file to download.
* @param {string} path - The local file path where the recording will be saved.
* @return {Promise<void>} A promise that resolves when the recording has been successfully downloaded.
*
Expand All @@ -602,7 +645,7 @@ export class Voice extends Client {
/**
* Download the transcription of a call to the specified file path.
*
* @param {string} file - The name of the transcription file to download.
* @param {string} file - The name or transcription id of the recording file to download.
* @param {string} path - The local file path where the transcription will be saved.
* @return {Promise<void>} A promise that resolves when the transcription has been successfully downloaded.
*
Expand Down

0 comments on commit 72f9472

Please sign in to comment.