Comments (5)
Annotated Source Code
based on a snapshot of Simple Menu TwiML source code (menu.php)
sent to me by Twilio support by email on 2019-07-08
This is PHP code.
<?php
It is shared by Twilio under the MIT license.
/*
Copyright (c) 2012 Twilio, Inc.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
It uses the (deprecated) class Twilio\Twiml of the twilio-php helper library to generate TwiML.
use Twilio\Twiml;
Start a new TwiML document.
// initiate response library
$response = new Twiml();
Make sure that Options
is an array. Thanks to this code, if no options were provided, an empty array will be created. This avoids errors further down the line, but having a menu without any options is of little use. It may be better to just fail in that case.
// init Options as array, if it's not already
if (!is_array($_REQUEST['Options'])) {
$_REQUEST['Options'] = array($_REQUEST['Options']);
}
// remove empty entries from PhoneNumbers
$_REQUEST['Options'] = array_filter($_REQUEST['Options']);
Stage 2
// if DialStatus was sent, it means we got here after a Dial attempt
if (strlen($_REQUEST['Digits'])) {
// if valid option given, the redirect
if (strlen($location = $_REQUEST['Options'][$_REQUEST['Digits']])) {
header('Location: ' . $location);
die;
} else {
// answered call, so just hangup
$response->say("I'm sorry, that wasn't a valid option.");
}
}
Stage 1
// calculate the max number of digits we need to wait for
$maxDigits = 1;
foreach ($_REQUEST['Options'] as $key => $value) {
$maxDigits = max($maxDigits, strlen($key));
}
// add a gather with numDigits
$gather = $response->gather(array(
'numDigits' => $maxDigits,
));
// play the greeting while accepting digits
// figure out the message
// first, check to see if we have an http URL (simple check)
if (strtolower(substr(trim($_GET['Message']), 0, 4)) == 'http') {
$gather->play($_GET['Message']);
}
// read back the message given
elseif (strlen($_GET['Message'])) {
$gather->say(stripslashes($_GET['Message']));
}
// add a redirect if nothing was pressed
$response->redirect();
Send the XML content-type header and the TwiML body.
// send the response
if (!headers_sent()) {
header('Content-type: text/xml');
}
echo $response;
from function-templates.
Test Cases
[SIMPLE-MENU-1-1] Recorded Message
Input
Parameter | Value |
---|---|
Message |
https://example.com/recorded-message.mp3 |
Output
$ curl -s 'https://twimlets.com/menu?Message=https%3A%2F%2Fexample.com%2Frecorded-message.mp3' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Play>https://example.com/recorded-message.mp3</Play>
</Gather>
<Redirect/>
</Response>
[SIMPLE-MENU-1-2] Text Message
Input
Parameter | Value |
---|---|
Message |
Text message |
Output
$ curl -s 'https://twimlets.com/menu?Message=Text%20message' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say>Text message</Say>
</Gather>
<Redirect/>
</Response>
[SIMPLE-MENU-1-3] Multiple Digits to Gather
Input
Parameter | Value |
---|---|
Message |
Text message |
Options[12345] |
https://example.com/12345 |
Output
$ curl -s 'https://twimlets.com/menu?Message=Text%20message&Options%5B12345%5D=https%3A%2F%2Fexample.com%2F12345' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="5">
<Say>Text message</Say>
</Gather>
<Redirect/>
</Response>
from function-templates.
[SIMPLE-MENU-2-1] Digits Pressed Match an Option
Input
Parameter | Value |
---|---|
Digits |
12345 |
Options[12345] |
https://example.com/12345 |
Output
Use -I
option of cURL to capture headers without following the redirect:
$ curl -I -s 'https://twimlets.com/menu?Digits=12345&Options%5B12345%5D=https%3A%2F%2Fexample.com%2F12345'
HTTP/2 302
date: Wed, 17 Jul 2019 20:03:40 GMT
content-type: text/html
location: https://example.com/12345
server: nginx
x-shenanigans: none
x-shenanigans: none
[SIMPLE-MENU-2-2] Digits Pressed Do Not Match Any Option
Input
Parameter | Value |
---|---|
Digits |
42 |
Output
$ curl -s 'https://twimlets.com/menu?Digits=42' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>I'm sorry, that wasn't a valid option.</Say>
<Gather numDigits="1"/>
<Redirect/>
</Response>
from function-templates.
Unit Tests
PASS funlet-simple-menu/funlet-simple-menu.test.js
✓ [SIMPLE-MENU-INPUT-MESSAGE-1] Read Message from Event (3ms)
✓ [SIMPLE-MENU-INPUT-MESSAGE-2] Read Message from Environment (1ms)
✓ [SIMPLE-MENU-INPUT-MESSAGE-3] Read Default Message from Script
✓ [SIMPLE-MENU-INPUT-ERROR-MESSAGE-1] Read Error Message from Event (2ms)
✓ [SIMPLE-MENU-INPUT-ERROR-MESSAGE-2] Read Error Message from Environment (1ms)
✓ [SIMPLE-MENU-INPUT-ERROR-MESSAGE-3] Read Default Error Message from Script
✓ [SIMPLE-MENU-INPUT-LANGUAGE-1] Read Language from Event
✓ [SIMPLE-MENU-INPUT-LANGUAGE-2] Read Language from Environment
✓ [SIMPLE-MENU-INPUT-LANGUAGE-3] Read Default Language from Script (1ms)
✓ [SIMPLE-MENU-INPUT-VOICE-1] Read Voice from Event
✓ [SIMPLE-MENU-INPUT-VOICE-2] Read Voice from Environment
✓ [SIMPLE-MENU-INPUT-VOICE-3] Read Default Voice from Script
✓ [SIMPLE-MENU-INPUT-OPTIONS-1] Read Single Option from Event (1ms)
✓ [SIMPLE-MENU-INPUT-OPTIONS-2] Read Sequential List of Options from Event
✓ [SIMPLE-MENU-INPUT-OPTIONS-3] Read List of Keys/Actions from Event
✓ [SIMPLE-MENU-INPUT-OPTIONS-4] Read Single Option from Environment (1ms)
✓ [SIMPLE-MENU-INPUT-OPTIONS-5] Read Single Key/Action from Environment
✓ [SIMPLE-MENU-INPUT-OPTIONS-6] Read Key/Action List from Environment
✓ [SIMPLE-MENU-INPUT-OPTIONS-7] Read Default Options from Script
✓ [SIMPLE-MENU-INPUT-DIGITS-0] Read No Digits from Event (1ms)
✓ [SIMPLE-MENU-INPUT-DIGITS-1] Read Non-Empty Digits from Event
✓ [SIMPLE-MENU-OUTPUT-GATHER-DIGITS-0] Gather Digits Without Message (2ms)
✓ [SIMPLE-MENU-OUTPUT-GATHER-DIGITS-1] Gather Digits With Recorded Message (1ms)
✓ [SIMPLE-MENU-OUTPUT-GATHER-DIGITS-2] Gather Digits With Text Message
✓ [SIMPLE-MENU-OUTPUT-SIMPLE-MENU-1-3] Multiple Digits to Gather
✓ [SIMPLE-MENU-OUTPUT-SIMPLE-MENU-2-0] No Digits (1ms)
✓ [SIMPLE-MENU-OUTPUT-SIMPLE-MENU-2-1] Digits Pressed Match an Option
✓ [SIMPLE-MENU-OUTPUT-SIMPLE-MENU-2-2] Digits Pressed Do Not Match Any Option (1ms)
✓ [SIMPLE-MENU-1-2] Full Response: Text Message
✓ [SIMPLE-MENU-2-1] Full Response: Digits Pressed Match an Option (1ms)
from function-templates.
Integration Tests
With https://$DOMAIN.twil.io/menu
the URL of a copy of the Simple Message Funlet deployed in my Twilio account with checks for signed requests disabled:
$ ./test-simple-menu.sh "https://$DOMAIN.twil.io/menu"
[SIMPLE-MENU-1-1] Recorded Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Play>https://example.com/recorded-message.mp3</Play>
</Gather>
<Redirect/>
</Response>
[SIMPLE-MENU-1-2] Text Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say language="en" voice="alice">Text message</Say>
</Gather>
<Redirect/>
</Response>
[SIMPLE-MENU-1-3] Multiple Digits to Gather
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="5">
<Say language="en" voice="alice">Text message</Say>
</Gather>
<Redirect/>
</Response>
[SIMPLE-MENU-2-1] Digits Pressed Match an Option
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Redirect>https://example.com/12345</Redirect>
</Response>
[SIMPLE-MENU-2-2] Digits Pressed Do Not Match Any Option
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="en" voice="alice">I'm sorry, that wasn't a valid option.</Say>
<Gather numDigits="1"/>
<Redirect/>
</Response>
The diff with the Twimlet output shows two types of differences:
$ ./test-simple-menu.sh "https://$DOMAIN.twil.io/menu" | diff twimlet-simple-menu.log -
14c14
< <Say>Text message</Say>
---
> <Say language="en" voice="alice">Text message</Say>
23c23
< <Say>Text message</Say>
---
> <Say language="en" voice="alice">Text message</Say>
29c29,32
< Redirect: https://example.com/12345
---
> <?xml version="1.0" encoding="UTF-8"?>
> <Response>
> <Redirect>https://example.com/12345</Redirect>
> </Response>
34c37
< <Say>I'm sorry, that wasn't a valid option.</Say>
---
> <Say language="en" voice="alice">I'm sorry, that wasn't a valid option.</Say>
- the
language
andvoice
are provided in<Say>
- a TwiML
<Redirect>
is used in place of an HTTP redirect.
from function-templates.
Related Issues (20)
- Finish explaining protected functions in the contributing guide
- cannot install the plugin - error [email protected]: The engine "node" is incompatible with this module HOT 3
- Unclear SMS Notifications UI
- Link to `LICENSE` in PR template is a 404 HOT 2
- The template "video" doesn't exist HOT 6
- Debugging help request (simulring funlet) HOT 3
- Verify sample app error HOT 5
- SIP Quickstart: SIP credential password requirements not obvious
- Passcode error on the video sample HOT 5
- SIP Quickstart: /admin/index.html unable to login (error 403 unauthorised) HOT 1
- Unable to find the css code for the classes used in the "verify" function-template HOT 2
- Assign Reviews action doesn't work
- Sid: event.Sid -> Sid: event.SmsSid HOT 3
- Install Issue HOT 1
- Simulring Error Line 146
- iPhone video problem using the code exchange Basic Video Chat (severless) HOT 1
- Migration of 'forward-message-sendgrid' to @sendgrid/mail due to ES Module incompatibility with Twilio Functions HOT 1
- // Install the C# / .NET helper library from twilio.com/docs/csharp/install using System; using Twilio; using Twilio.Rest.Verify.V2.Service; class Program { static void Main(string[] args) { // Find your Account SID and Auth Token at twilio.com/console // and set the environment variables. See http://twil.io/secure string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID"); string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN"); TwilioClient.Init(accountSid, authToken); var verification = VerificationResource.Create( to: "+15017122661", channel: "sms", pathServiceSid: "VAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ); Console.WriteLine(verification.Status); } } HOT 1
- How to retrieve date and time HOT 1
- Notify Services Depreciated HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from function-templates.