# Example 3: Call and Bridge (with Voice Focus) [Previous Lesson](../call-me-back/README.md) [Next Lesson](../call-lex-bot/README.md) ## Overall Behavior The application will answer the phone, play a message prompting you to enter a phone number, collect those digits, dial that number, and when connected will listen for a 1 to enable Amazon Voice Focus, or a 0 to disable it. Like in our previous example, the code will use Amazon Polly for voice prompts. We do include a recording of a "ringing tone" (ringback tone, in PSTN terms) to provide indication that the called number is ringing. ## Operation This example application takes advantage of the "Call Bridging" feature of PSTN Audio. The call is first answered, but instead of a Speak action event we will use SpeakAndGetDigits. This will play the voice and collect keypad presses. ```typescript function newCall(event: any) { const callId = event.CallDetails.Participants[0].CallId; speakCollectDigitsAction.Parameters.CallId = callId; speakCollectDigitsAction.Parameters.InputDigitsRegex = "^[1][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]"; speakCollectDigitsAction.Parameters.SpeechParameters.Text = "Hello! Please enter the number you would like to call, starting with a one followed by ten digits"; return [pauseAction, speakCollectDigitsAction]; } ``` The regex controls what pattern of digits we expect. In this case we expect a 1 followed by 10 numbers. It's worth looking at the rest of the object though: ```typescript const speakCollectDigitsAction = { Type: "SpeakAndGetDigits", Parameters: { CallId: "call-id-1", // required InputDigitsRegex: "", // optional SpeechParameters: { Text: "Hello World", // required Engine: "neural", // optional. Defaults to standard LanguageCode: "en-US", // optional TextType: "ssml", // optional VoiceId: "Joanna", // optional. Defaults to Joanna }, FailureSpeechParameters: { Text: "Ooops, there was an error.", // required Engine: "neural", // optional. Defaults to the Engine value in SpeechParameters LanguageCode: "en-US", // optional. Defaults to the LanguageCode value in SpeechParameters TextType: "ssml", // optional. Defaults to the TextType value in SpeechParameters VoiceId: "Joanna", // optional. Defaults to the VoiceId value in SpeechParameters }, MinNumberOfDigits: 11, // optional MaxNumberOfDigits: 11, // optional TerminatorDigits: ["#"], // optional InBetweenDigitsDurationInMilliseconds: 5000, // optional Repeat: 3, // optional RepeatDurationInMilliseconds: 10000 // required } } ``` The parameters follow the examples in the Amazon Polly documentation for voices, and the [PlayAudioAndGetDigits](https://docs.aws.amazon.com/chime/latest/dg/play-audio-get-digits.html) for digit control. When the digits are collected, they are delivered in an ACTION_SUCCESSFUL event. Since that message is a common one, we inspect the event.ActionData.Type. If it's the SpeakAndGetDigits result we can read the digits from the event and "bridge" the call. Note that this does not call an API, this is a returned action message. ```typescript function placeCall(event: any) { callAndBridgeAction.Parameters.CallerIdNumber = event.CallDetails.Participants[0].From; callAndBridgeAction.Parameters.RingbackTone.Key = "US_ringback_tone.wav"; callAndBridgeAction.Parameters.Endpoints[0].Uri = "+" + event.ActionData.ReceivedDigits; return [pauseAction, callAndBridgeAction]; } ``` It's worth looking at the callAndBridgeAction object: ```typescript const callAndBridgeAction = { Type: "CallAndBridge", Parameters: { CallTimeoutSeconds: 30, CallerIdNumber: "e164PhoneNumber", // required RingbackTone: { // optional Type: "S3", BucketName: wavFileBucket, Key: "audio_file_name" }, Endpoints: [ { Uri: "e164PhoneNumber", // required BridgeEndpointType: "PSTN" // required } ], CustomSipHeaders: { String: "String" } } } ``` The key fields to notice is the RingbackTone object. We have set the S3 bucket (exactly as we did in the first lesson) with a normal ringback tone recording. Bridging calls does not result in NEW_OUTBOUND_CALL, RINGING, or ANSWERED events. Instead, the ACTION_SUCCESSFUL message with the event.ActionData.Type field set to "CallAndBridge" will signal success. You will note that we set Amazon voice Focus to be off for this call. However, to enable a caller to toggle it on and off, we add another step. We do a [ReceiveDigits](https://docs.aws.amazon.com/chime/latest/dg/listen-to-digits.html) to collect one (and only one) number, either a 0 or a 1. You will note the function: ```typescript function connectCall(event: any) { const callId = findParticipantCallId(event, "Inbound"); const callId2 = findParticipantCallId(event, "Outbound"); // disable Voice Focus initially voiceFocusAction.Parameters.CallId = callId; voiceFocusAction.Parameters.Enable = false; voiceFocusAction2.Parameters.CallId = callId2; voiceFocusAction2.Parameters.Enable = false; receiveDigitsAction.Parameters.CallId = callId; receiveDigitsAction.Parameters.InputDigitsRegex = "[0-1]$"; return [voiceFocusAction, voiceFocusAction2, receiveDigitsAction]; } ``` Setting the Regex to "[0-1]$" will ensure that we only get a 0 or a 1. Since this is a direct action not part of another action, we will get DIGITS_RECEIVED events when keypresses occur and we process them like this: ```typescript function digitsReceived(event: any) { let actions: any; if (event.ActionData.Type = "ReceivedDigits") { voiceFocusAction.Parameters.CallId = findParticipantCallId(event, "Inbound"); voiceFocusAction2.Parameters.CallId = findParticipantCallId(event, "Outbound"); switch (event.ActionData.ReceivedDigits) { case "0": voiceFocusAction.Parameters.Enable = false; voiceFocusAction2.Parameters.Enable = false; break; case "1": voiceFocusAction.Parameters.Enable = true; voiceFocusAction2.Parameters.Enable = true; break; } receiveDigitsAction.Parameters.CallId = findParticipantCallId(event, "Inbound"); receiveDigitsAction.Parameters.InputDigitsRegex = "[0-1]$"; actions = [voiceFocusAction, voiceFocusAction2, receiveDigitsAction]; return actions; } } ``` Depending on if a 0 or 1 is pressed, we enable or disable Voice Focus on both the inbound and outbound leg of the call. Hangups will be processed as seen in prior examples. ## Call Sequence Diagram (visible only on GitHub) ```mermaid sequenceDiagram autonumber PSTN-Audio->>Lambda-App: NEW_INBOUND_CALL Lambda-App->>PSTN-Audio: pauseAction, SpeakAndGetDigits PSTN-Audio->>Lambda-App: ACTION_SUCCESSFUL Lambda-App->>PSTN-Audio: pauseAction, callAndBridgeAction PSTN-Audio->>Lambda-App: ACTION_SUCCESSFUL Lambda-App->>PSTN-Audio: voiceFocusAction, voiceFocusAction2, receiveDigitsAction PSTN-Audio->>Lambda-App: HANGUP ``` ## Try It Assuming you have already deployed the parent directory and provisioned Amazon Chime SDK PSTN resources, you can deploy this lambda and test it like this: ```bash yarn deploy yarn swap ``` As we learned in the [CDK Overview](../../docs/cdk-overview/) "yarn deploy" will do a CDK deploy of the lambda and "yarn swap" will do an "aws chime update-sip-media-application" command to set this lambda as the one that will be invoked when a call arrives. You can see the CloudWatch log group using the command ```bash yarn group ``` You can remind yourself of your PSTN Audio phone number with ```bash yarn number ``` ## Provisioning Notes As im the first example, we provision S3 storage to support holding wave files. However, in this case it's not voice prompts but rather a "ringing tone" (ringback tone) to indicate that the called number is ringing. However, since CallandBridge does not result in events sent to the lambda, we provide that in the action itself. The skeleton object in the code looks like this: ```typescript const callAndBridgeAction = { Type: "CallAndBridge", Parameters: { CallTimeoutSeconds: 30, CallerIdNumber: "e164PhoneNumber", // required RingbackTone: { // optional Type: "S3", BucketName: wavFileBucket, Key: "audio_file_name" }, Endpoints: [ { Uri: "e164PhoneNumber", // required BridgeEndpointType: "PSTN" // required } ], CustomSipHeaders: { String: "String" } } } ``` You can see that you place the ringback file in the "Key" field. This file is deployed to S3 by the CDK scripts like we did in the [previous lesson](../call-me-back/README.md). You can get more information on the CDK deployment scripts in the [How It Works](../../docs/how-it-works/) section. [Previous Lesson](../call-and-bridge/README.md) [Next Lesson](../call-lex-bot/README.md)