Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions voice-ivr/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
# Voice IVR

Navigate a IVR (phone tree) using keys or speech-to-text
This application allows users to navigate an IVR (phone tree) using keys or speech-to-text via a Twilio number. When a user calls the number, they are presented with three options: Talk to Sales, Hours of Operation, or Address. Selecting the first option forwards the call to a given phone number. The second provides an immediate voice response with relevant information. Choosing the third option triggers an SMS with the requested details. The application is fully customizable, allowing you to edit the available options and responses.

## Pre-requisites

- A Twilio account - [sign up here](https://www.twilio.com/try-twilio)
- A Twilio phone number

### Environment variables

This project requires some environment variables to be set. To keep your tokens and secrets secure, make sure to not commit the `.env` file in git. When setting up the project with `twilio serverless:init ...` the Twilio CLI will create a `.gitignore` file that excludes `.env` from the version history.

In your `.env` file, set the following values:

| Variable | Description | Required |
| :------- | :---------- | :------- |
MY_PHONE_NUMBER Your phone number true
| Variable | Meaning | Required |
| :---------------- | :------------------------------------------------------- | :------- |
| `MY_PHONE_NUMBER` | The phone number that you want calls to be forwarded to. | yes |

### Function Parameters

`/voice-ivr` is protected and requires a valid Twilio signature as well as the following parameters:

| Parameter | Description | Required |
| :-------- | :---------- | :------- |

`/voice-ivr` is protected and requires a valid Twilio signature.

`/handle-user-input` is protected and requires a valid Twilio signature as well as the following parameters:
`/handle-user-input` is protected and requires a valid Twilio signature and expects the following parameters:

| Parameter | Description | Required |
| :------------- | :------------------------------- | :------- |
Expand Down
209 changes: 125 additions & 84 deletions voice-ivr/assets/index.html
Original file line number Diff line number Diff line change
@@ -1,90 +1,131 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Get started with your Twilio Functions!</title>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Get started with your Twilio Functions!</title>

<link rel="icon" href="https://twilio-labs.github.io/function-templates/static/v1/favicon.ico">
<link rel="stylesheet" href="https://twilio-labs.github.io/function-templates/static/v1/ce-paste-theme.css">
<link
rel="icon"
href="https://twilio-labs.github.io/function-templates/static/v1/favicon.ico"
/>
<link
rel="stylesheet"
href="https://twilio-labs.github.io/function-templates/static/v1/ce-paste-theme.css"
/>

<script src="https://twilio-labs.github.io/function-templates/static/v1/ce-helpers.js" defer></script>
<script>
window.addEventListener('DOMContentLoaded', (_event) => {
inputPrependBaseURL();
});
</script>
</head>
<body>
<div class="page-top">
<header>
<div id="twilio-logo">
<a href="https://www.twilio.com/" target="_blank" rel="noopener">
<svg class="logo" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 60 60">
<title>Twilio Logo</title><path class="cls-1" d="M30,15A15,15,0,1,0,45,30,15,15,0,0,0,30,15Zm0,26A11,11,0,1,1,41,30,11,11,0,0,1,30,41Zm6.8-14.7a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,26.3Zm0,7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,33.7Zm-7.4,0a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,33.7Zm0-7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,26.3Z"/></svg>
</a>
</div>
<nav>
<span>Your Twilio application</span>
<aside>
<svg class="icon" role="img" aria-hidden="true" width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby="NewIcon-1577"><path fill="currentColor" fill-rule="evenodd" d="M6.991 7.507c.003-.679 1.021-.675 1.019.004-.012 2.956 1.388 4.41 4.492 4.48.673.016.66 1.021-.013 1.019-2.898-.011-4.327 1.446-4.48 4.506-.033.658-1.01.639-1.018-.02-.03-3.027-1.382-4.49-4.481-4.486-.675 0-.682-1.009-.008-1.019 3.02-.042 4.478-1.452 4.49-4.484zm.505 2.757l-.115.242c-.459.9-1.166 1.558-2.115 1.976l.176.08c.973.465 1.664 1.211 2.083 2.22l.02.05.088-.192c.464-.973 1.173-1.685 2.123-2.124l.039-.018-.118-.05c-.963-.435-1.667-1.117-2.113-2.034l-.068-.15zm10.357-8.12c.174.17.194.434.058.625l-.058.068-1.954 1.905 1.954 1.908a.482.482 0 010 .694.512.512 0 01-.641.056l-.07-.056-1.954-1.908-1.954 1.908a.511.511 0 01-.71 0 .482.482 0 01-.058-.626l.058-.068 1.954-1.908-1.954-1.905a.482.482 0 010-.693.512.512 0 01.64-.057l.07.057 1.954 1.905 1.954-1.905a.511.511 0 01.71 0z"></path></svg>
Live
</aside>
</nav>
</header>
<script
src="https://twilio-labs.github.io/function-templates/static/v1/ce-helpers.js"
defer
></script>
<script>
window.addEventListener('DOMContentLoaded', (_event) => {
inputPrependBaseURL();
});
</script>
</head>
<body>
<div class="page-top">
<header>
<div id="twilio-logo">
<a href="https://www.twilio.com/" target="_blank" rel="noopener">
<svg
class="logo"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 60 60"
>
<title>Twilio Logo</title>
<path
class="cls-1"
d="M30,15A15,15,0,1,0,45,30,15,15,0,0,0,30,15Zm0,26A11,11,0,1,1,41,30,11,11,0,0,1,30,41Zm6.8-14.7a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,26.3Zm0,7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,36.8,33.7Zm-7.4,0a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,33.7Zm0-7.4a3.1,3.1,0,1,1-3.1-3.1A3.12,3.12,0,0,1,29.4,26.3Z"
/>
</svg>
</a>
</div>
<main>
<div class="content">
<h1>
<img src="https://twilio-labs.github.io/function-templates/static/v1/success.svg" />
<div>
<p>Welcome!</p>
<p>Your live application with Twilio is ready to use!</p>
</div>
</h1>
<section>
<h2>Get started with your application</h2>
<p>
Follow these steps to try out your new app:
</p>
<p>
This app presents callers with an IVR (phone tree) whose options can be selected using
either phone keypad digits or the caller's voice.
</p>
<ol class="steps">
<li>Call your Twilio phone number</li>
<li>You should be greeted by a voice listing all options</li>
<li>Press any of the mentioned digits or try saying the words</li>
</ol>
</section>
<nav>
<span>Your Twilio application</span>
<aside>
<svg
class="icon"
role="img"
aria-hidden="true"
width="100%"
height="100%"
viewBox="0 0 20 20"
aria-labelledby="NewIcon-1577"
>
<path
fill="currentColor"
fill-rule="evenodd"
d="M6.991 7.507c.003-.679 1.021-.675 1.019.004-.012 2.956 1.388 4.41 4.492 4.48.673.016.66 1.021-.013 1.019-2.898-.011-4.327 1.446-4.48 4.506-.033.658-1.01.639-1.018-.02-.03-3.027-1.382-4.49-4.481-4.486-.675 0-.682-1.009-.008-1.019 3.02-.042 4.478-1.452 4.49-4.484zm.505 2.757l-.115.242c-.459.9-1.166 1.558-2.115 1.976l.176.08c.973.465 1.664 1.211 2.083 2.22l.02.05.088-.192c.464-.973 1.173-1.685 2.123-2.124l.039-.018-.118-.05c-.963-.435-1.667-1.117-2.113-2.034l-.068-.15zm10.357-8.12c.174.17.194.434.058.625l-.058.068-1.954 1.905 1.954 1.908a.482.482 0 010 .694.512.512 0 01-.641.056l-.07-.056-1.954-1.908-1.954 1.908a.511.511 0 01-.71 0 .482.482 0 01-.058-.626l.058-.068 1.954-1.908-1.954-1.905a.482.482 0 010-.693.512.512 0 01.64-.057l.07.057 1.954 1.905 1.954-1.905a.511.511 0 01.71 0z"
></path>
</svg>
Live
</aside>
</nav>
</header>
</div>
<main>
<div class="content">
<h1>
<img
src="https://twilio-labs.github.io/function-templates/static/v1/success.svg"
/>
<div>
<p>Welcome!</p>
<p>Your live application with Twilio is ready to use!</p>
</div>
</h1>
<section>
<h2>Get started with your application</h2>
<p>Follow these steps to try out your new app:</p>
<p>
This app presents callers with an IVR (phone tree) whose options can
be selected using either phone keypad digits or the caller's voice.
</p>
<ol class="steps">
<li>Call your Twilio phone number</li>
<li>You should be greeted by a voice listing all options</li>
<li>Press any of the mentioned digits or try saying the words</li>
</ol>
</section>

<section>
<!-- APP_INFO_V2 -->
</section>
<section>
<h2>Troubleshooting</h2>
<ul>
<li>
Check the
<a href="https://www.twilio.com/console/phone-numbers/incoming"
target="_blank"
rel="noopener">
phone number configuration
</a>
and make sure the Twilio phone number you want for your app has a voice webhook
configured to point at the following URL
<form>
<label for="twilio-webhook">Webhook URL</label>
<input type="text" id="twilio-webhook" class="function-root" readonly=true value="/voice-ivr">
</form>
</li>
</ul>
</section>
</div>
</main>
<footer>
<span class="statement">We can't wait to see what you build.</span>
</footer>
</body>
<section>
<!-- APP_INFO_V2 -->
</section>
<section>
<h2>Troubleshooting</h2>
<ul>
<li>
Check the
<a
href="https://www.twilio.com/console/phone-numbers/incoming"
target="_blank"
rel="noopener"
>
phone number configuration
</a>
and make sure the Twilio phone number you want for your app has a
voice webhook configured to point at the following URL
<form>
<label for="twilio-webhook">Webhook URL</label>
<input
type="text"
id="twilio-webhook"
class="function-root"
readonly="true"
value="/voice-ivr"
/>
</form>
</li>
</ul>
</section>
</div>
</main>
<footer>
<span class="statement">We can't wait to see what you build.</span>
</footer>
</body>
</html>
3 changes: 2 additions & 1 deletion voice-ivr/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

## [1.0.0]

### Added
- Initial release.

- Initial release.
83 changes: 55 additions & 28 deletions voice-ivr/functions/handle-user-input.protected.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
function sendMessage(context, event) {
/**
* handle-user-input Function
*
* Description:
* This file contains the input handler to the Voice IVR Function template.
* The incoming phone call will be transferred from the voice-ivr Function
* to this function using the Gather TwiML verb which will also pass in the
* users input. This Function will read the users input and respond
* back depending on the users selection.
*
*
* Contents:
* 1. SMS Handler
* 2. Main Handler
*/

/**
* 1. SMS Handler
*
* This function will send an SMS of the address to the caller
* if the address option was chosen.
*
*/

async function sendMessage(context, event) {
const client = context.getTwilioClient();
return client.messages
.create({
from: event.To,
to: event.From,
body: 'Here is our address: 375 Beale St #300, San Francisco, CA 94105, USA',
})
.then(
(resp) => resp,
(err) => {
console.log(err);
return Promise.resolve();
}
);
return client.messages.create({
from: event.To,
to: event.From,
body: 'Here is our address: 375 Beale St #300, San Francisco, CA 94105, USA',
});
}

/**
* Handles the user input gathered in the voice-ivr Function
* 1. Main Handler
*
* This Twilio Function will create a new Voice Response using Twiml
* based on the users input from the voice-ivr Function.
*
* The function will fetch the user inputs by reading the Digits or
* SpeechResult variables from the incoming request parameters. If the
* user input was given by speech-to-text, it will convert it to its digit
* counterpart.
*
* If option 1 (sales) was chosen, the function will forward the call
* and dial the MY_PHONE_NUMBER specified in /.env. If option 2
* (opening hours) was chosen, the Voice Response will respond to the
* caller with the opening hours. If option 3 (address) was chosen,
* the SMS handler will be executed and text the address to the caller.
* If any other input was chosen, the call will go in a loop and redirect
* the call to the voice-ivr Function.
*
*/
// eslint-disable-next-line consistent-return
exports.handler = function (context, event, callback) {

exports.handler = async function (context, event, callback) {
let UserInput = event.Digits || event.SpeechResult;
const twiml = new Twilio.twiml.VoiceResponse();

Expand Down Expand Up @@ -60,16 +93,10 @@ exports.handler = function (context, event, callback) {
twiml.redirect('voice-ivr');
}

let request = Promise.resolve();
if (UserInput === '3') {
request = sendMessage(context, event);
try {
if (UserInput === '3') await sendMessage(context, event);
} catch (err) {
console.log(err);
}

request
.then(() => {
return callback(null, twiml);
})
.catch((err) => {
return callback(err);
});
return callback(null, twiml);
};
20 changes: 19 additions & 1 deletion voice-ivr/functions/voice-ivr.protected.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
/**
* Returns TwiML that prompts the users to make a choice.
* Voice IVR
*
* Description:
* This file contains the entry point to the Voice IVR function template
* which will prompt the user with an IVR phone tree with three options.
*
*
* Contents:
* 1. Main Handler
*/

/**
* 1. Main Handler
*
* This is the entry point to your Twilio Function, which will create
* and return a TwiML Voice Response that will prompt the user with
* three options: Talk to Sales, Hours of Operation, or Address.
* If the user enters something it will trigger the handle-user-input Function and otherwise go in a loop.
*
*/

exports.handler = function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
const gather = twiml.gather({
Expand Down
Loading