Comments (17)
Find me was one I built a little while back and you can see it here: https://github.com/twilio-labs/function-templates/tree/master/hunt.
I didn't specifically build it to replicate the twimlet exactly (numbers are set in an environment variable, for example) but it might be a good start for building out this twimlet replacement.
from function-templates.
numbers are set in an environment variable, for example
This is a good point.
After careful thought, I am considering to use the following guidelines for the new function templates:
Level of Properties | Shared by |
---|---|
environment | all function templates: account parameters |
script constants | all instances of the script: localization strings, default values |
script parameters | a single instance of the script: values specific to the task |
It may be useful to cross these boundaries in some cases:
- use a script parameter, if provided,
- otherwise default to an environment parameter, if it exists,
- or else fallback to a default value declared as a constant in the script.
from function-templates.
I think that's a good guideline. Largely because Functions in the Twilio Console for example can be selected for a phone number without having to know the URL. Therefore query (script) parameters would be difficult to specify. At the same time having them to specify dynamic behavior was one of the most useful features of Twimlets.
from function-templates.
Annotated Source Code
based on a snapshot of Find Me TwiML source code (findme.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 PhoneNumbers
is an array. Thanks to this code, if you provide only one phone number, it is possible to omit the square brackets from the parameter name and write just PhoneNumbers=916-555-0123
instead of PhoneNumbers[]=916-555-0123
.
// init as array, if it's not
if (!is_array($_REQUEST['PhoneNumbers'])) {
$_REQUEST['PhoneNumbers'] = array($_REQUEST['PhoneNumbers']);
}
Restrict the list to 10 non-empty phone numbers.
While writing the corresponding test case, I found a bug 🐞 in this code. While it checks that no more than 10 numbers were given, it then keeps only the numbers above the limit of 10. How is that possible? I checked the documentation of the PHP function array_splice
:
array_splice(array, 10)
correctly removes all items starting from item 10 until the end of the array- but it changes the array itself
- and it returns the removed items
- which are here assigned back to the original array
As a result, only the removed items are preserved.
// remove empty entries from PhoneNumbers
$_REQUEST['PhoneNumbers'] = @array_filter($_REQUEST['PhoneNumbers']);
// verify no more than 10 numbers given
if (count($_REQUEST['PhoneNumbers']) > 10) {
$_REQUEST['PhoneNumbers'] = array_splice($_REQUEST['PhoneNumbers'], 10);
}
Stage 4
// if The Dial flag is present, it means we're returning from an attempted Dial
if (isset($_REQUEST['Dial']) && ($_REQUEST['DialStatus'] == 'answered' || $_REQUEST['DialCallStatus'] == 'completed')) {
// answered call, so just hangup
$response->hangup();
Stage 1
} else {
// No dial flag, or anything other than "answered", roll on to the next (or first, as it may be) number
// get the next number of the array
if (!$nextNumber = @array_shift($_REQUEST['PhoneNumbers'])) {
// if no phone numbers left, redirect to the FailUrl
// FailUrl found, so redirect and kill the cookie
if (strlen($_REQUEST['FailUrl'])) {
header('Location: ' .$_REQUEST['FailUrl']);
die;
} else {
// no FailUrl found, so just end the call
$response->hangup();
}
} else {
// re-assemble remaining numbers into a QueryString, shifting the 0th off the array
$qs = 'FailUrl=' . urlencode($_REQUEST['FailUrl']) . '&Timeout=' . urlencode($_REQUEST['Timeout']) . '&Message=' . urlencode($_REQUEST['Message']);
foreach ($_REQUEST['PhoneNumbers'] as $number) {
$qs .= '&PhoneNumbers%5B%5D=' . urlencode($number);
}
// add a dial to the response
$dial = $response->dial(array(
'action' => $_SERVER['SCRIPT_URL'] . '?Dial=true&' . $qs,
'timeout' => $_REQUEST['Timeout'] ? $_REQUEST['Timeout'] : 60,
));
// add the number to dial
$dial->number($nextNumber, array(
Stage 2 and Stage 3: Whisper
Through the Whisper Twimlet, request the recipient to press a key to accept the incoming call (stage 2), then bridge the call when a digit has been pressed (stage 3). With the parameter HumanCheck
set to true, the call will be interrupted when a timeout of 5 seconds in <Gather>
expires without any digits pressed.
'url' => 'whisper?Message=' . urlencode($_REQUEST['Message']) . '&HumanCheck=1',
));
}
}
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
[FIND-ME-1-1] Find Me with 3 Numbers (from Example 1)
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
415-555-1212 |
PhoneNumbers[] |
415-555-1313 |
PhoneNumbers[] |
415-555-1414 |
Output
$ curl -s 'https://twimlets.com/findme?PhoneNumbers%5B%5D=415-555-1212&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-2] Find Me with 3 Numbers and Fallback URL (from Example 2)
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
415-555-1212 |
PhoneNumbers[] |
415-555-1313 |
PhoneNumbers[] |
415-555-1414 |
FailUrl |
https://example.com/please-try-later.mp3 |
Output
$ curl -s 'https://twimlets.com/findme?PhoneNumbers%5B%5D=415-555-1212&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414&FailUrl=https%3A%2F%2Fexample.com%2Fplease-try-later.mp3' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-3] Find Me with Custom Timeout and Message
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
415-555-1212 |
PhoneNumbers[] |
415-555-1313 |
PhoneNumbers[] |
415-555-1414 |
Timeout |
42 |
Message |
Custom message |
Output
$ curl -s 'https://twimlets.com/findme?PhoneNumbers%5B%5D=415-555-1212&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414&Timeout=42&Message=Custom%20message' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=42&Message=Custom+message&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
<Number url="whisper?Message=Custom+message&HumanCheck=1">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-4] Allow up to 10 numbers
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
415-555-1001 |
PhoneNumbers[] |
415-555-1002 |
PhoneNumbers[] |
415-555-1003 |
PhoneNumbers[] |
415-555-1004 |
PhoneNumbers[] |
415-555-1005 |
PhoneNumbers[] |
415-555-1006 |
PhoneNumbers[] |
415-555-1007 |
PhoneNumbers[] |
415-555-1008 |
PhoneNumbers[] |
415-555-1009 |
PhoneNumbers[] |
415-555-1010 |
Output
$ curl -s 'https://twimlets.com/findme?PhoneNumbers%5B%5D=415-555-0001&PhoneNumbers%5B%5D=415-555-0002&PhoneNumbers%5B%5D=415-555-0003&PhoneNumbers%5B%5D=415-555-0004&PhoneNumbers%5B%5D=415-555-0005&PhoneNumbers%5B%5D=415-555-0006&PhoneNumbers%5B%5D=415-555-0007&PhoneNumbers%5B%5D=415-555-0008&PhoneNumbers%5B%5D=415-555-0009&PhoneNumbers%5B%5D=415-555-0010' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-0002&PhoneNumbers%5B%5D=415-555-0003&PhoneNumbers%5B%5D=415-555-0004&PhoneNumbers%5B%5D=415-555-0005&PhoneNumbers%5B%5D=415-555-0006&PhoneNumbers%5B%5D=415-555-0007&PhoneNumbers%5B%5D=415-555-0008&PhoneNumbers%5B%5D=415-555-0009&PhoneNumbers%5B%5D=415-555-0010" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-0001</Number>
</Dial>
</Response>
[FIND-ME-1-5] Discard Extra Numbers Above 10
When more than 10 numbers are provided, the first one is called and only 9 are kept for the next try.
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
415-555-1001 |
PhoneNumbers[] |
415-555-1002 |
PhoneNumbers[] |
415-555-1003 |
PhoneNumbers[] |
415-555-1004 |
PhoneNumbers[] |
415-555-1005 |
PhoneNumbers[] |
415-555-1006 |
PhoneNumbers[] |
415-555-1007 |
PhoneNumbers[] |
415-555-1008 |
PhoneNumbers[] |
415-555-1009 |
PhoneNumbers[] |
415-555-1010 |
PhoneNumbers[] |
415-555-1011 |
PhoneNumbers[] |
415-555-1012 |
Output
$ curl -s 'https://twimlets.com/findme?PhoneNumbers%5B%5D=415-555-0001&PhoneNumbers%5B%5D=415-555-0002&PhoneNumbers%5B%5D=415-555-0003&PhoneNumbers%5B%5D=415-555-0004&PhoneNumbers%5B%5D=415-555-0005&PhoneNumbers%5B%5D=415-555-0006&PhoneNumbers%5B%5D=415-555-0007&PhoneNumbers%5B%5D=415-555-0008&PhoneNumbers%5B%5D=415-555-0009&PhoneNumbers%5B%5D=415-555-0010&PhoneNumbers%5B%5D=415-555-0011&PhoneNumbers%5B%5D=415-555-0012' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-0012" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-0011</Number>
</Dial>
</Response>
This is not the expected output. Due to a bug in the original code, the Find Me Twimlet keeps only the numbers beyond the 10th, instead of keeping only the first 10 numbers.
The output should actually be identical to the previous test case:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-0002&PhoneNumbers%5B%5D=415-555-0003&PhoneNumbers%5B%5D=415-555-0004&PhoneNumbers%5B%5D=415-555-0005&PhoneNumbers%5B%5D=415-555-0006&PhoneNumbers%5B%5D=415-555-0007&PhoneNumbers%5B%5D=415-555-0008&PhoneNumbers%5B%5D=415-555-0009&PhoneNumbers%5B%5D=415-555-0010" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-0001</Number>
</Dial>
</Response>
[FIND-ME-1-6] No More Numbers, Without Fallback URL
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
(empty or missing) |
Output
$ curl -s 'https://twimlets.com/findme' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
[FIND-ME-1-7] No More Numbers, With Fallback URL
Input
Parameter | Value |
---|---|
PhoneNumbers[] |
(empty or missing) |
FailUrl |
https://example.com/please-try-later.mp3 |
Output
Query only headers to avoid following the redirect:
$ curl -I -s 'https://twimlets.com/findme?FailUrl=https%3A%2F%2Fexample.com%2Fplease-try-later.mp3'
HTTP/2 302
date: Tue, 16 Jul 2019 21:27:14 GMT
content-type: text/html
location: https://example.com/please-try-later.mp3
server: nginx
x-shenanigans: none
x-shenanigans: none
from function-templates.
[FIND-ME-4-1] Call Answered
Input
Parameter | Value |
---|---|
Dial |
true |
DialStatus |
answered |
Output
$ curl -s 'https://twimlets.com/findme?Dial=true&DialStatus=answered' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
[FIND-ME-4-2] Call Completed
Input
Parameter | Value |
---|---|
Dial |
true |
DialCallStatus |
completed |
Output
$ curl -s 'https://twimlets.com/findme?Dial=true&DialCallStatus=completed' \
| xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
from function-templates.
Unit Tests
PASS funlet-find-me/funlet-find-me.test.js
✓ [FINDME-INPUT-PHONE-NUMBERS-1] Read Single Phone Number from Event (4ms)
✓ [FINDME-INPUT-PHONE-NUMBERS-2] Read List of Phone Numbers from Event
✓ [FINDME-INPUT-PHONE-NUMBERS-3] Read Single Phone Number from Environment (1ms)
✓ [FINDME-INPUT-PHONE-NUMBERS-4] Read Five Phone Numbers from Environment
✓ [FINDME-INPUT-PHONE-NUMBERS-5] Read Default Phone Numbers from Script
✓ [FINDME-INPUT-PHONE_NUMBERS-6] Skip empty values
✓ [FINDME-INPUT-TIMEOUT-1] Read Timeout from Event (1ms)
✓ [FINDME-INPUT-TIMEOUT-2] Read Timeout from Environment
✓ [FINDME-INPUT-TIMEOUT-3] Read Default Timeout from Script
✓ [FINDME-INPUT-DIAL-0] Read No Whisper from Event
✓ [FINDME-INPUT-DIAL-1] Read Whisper from Event (1ms)
✓ [FINDME-INPUT-MESSAGE-1] Read Message from Event
✓ [FINDME-INPUT-MESSAGE-2] Read Message from Environment
✓ [FINDME-INPUT-MESSAGE-3] Read Default Message from Script (with number in From parameter) (1ms)
✓ [FINDME-INPUT-MESSAGE-4] Read Default Message from Script (with number in Caller parameter)
✓ [FINDME-INPUT-LANGUAGE-1] Read Language from Event
✓ [FINDME-INPUT-LANGUAGE-2] Read Language from Environment
✓ [FINDME-INPUT-LANGUAGE-3] Read Default Language from Script (1ms)
✓ [FINDME-INPUT-VOICE-1] Read Voice from Event
✓ [FINDME-INPUT-VOICE-2] Read Voice from Environment
✓ [FINDME-INPUT-VOICE-3] Read Default Voice from Script
✓ [WHISPER-INPUT-HUMAN-CHECK-0] Read Human Check "1" from Event
✓ [FINDME-INPUT-HUMAN-CHECK-1] Read Human Check from Event (1ms)
✓ [FINDME-INPUT-HUMAN-CHECK-2] Read Human Check from Environment
✓ [FINDME-INPUT-HUMAN-CHECK-3] Read Default Human Check from Script
✓ [FINDME-INPUT-DIGITS-0] Read No Digits from Event
✓ [FINDME-INPUT-DIGITS-1] Read Empty Digits from Event
✓ [FINDME-INPUT-DIGITS-2] Read Non-Empty Digits from Event (1ms)
✓ [FINDME-INPUT-DIAL-0] Read No Dial from Event
✓ [FINDME-INPUT-DIAL-1] Read Dial from Event
✓ [FINDME-INPUT-CALL-STATUS-0] Read No Call Status from Event
✓ [FINDME-INPUT-CALL-STATUS-1] Read Answered Call Status from Event
✓ [FINDME-INPUT-CALL-STATUS-2] Read Completed Call Status from Event
✓ [FINDME-INPUT-CALL-STATUS-3] Read Busy Call Status from Event
✓ [FINDME-INPUT-FALLBACK-URL-1] Read Fallback URL from Event
✓ [FINDME-INPUT-FALLBACK-URL-2] Read Fallback URL from Environment
✓ [FINDME-INPUT-FALLBACK-URL-3] Read Default Fallback URL from Script (1ms)
✓ [FINDME-OUTPUT-FINDME-1-1] Find Me with 3 Phone Numbers (2ms)
✓ [FINDME-1-3] Find Me with Custom Timeout and Message (1ms)
✓ [FINDME-2-1] Whisper: Recorded Message (1ms)
✓ [FINDME-3-1] Whisper: A Digit was Pressed
✓ [FINDME-4-3] Failure with Fallback URL (1ms)
from function-templates.
Integration Tests
With https://$DOMAIN.twil.io/findme
the URL of a copy of the Find Me Funlet deployed in my Twilio account with checks for signed requests disabled:
$ ./test-find-me.sh "https://$DOMAIN.twil.io/findme"
[FIND-ME-1-1] Find Me with 3 Numbers (from Example 1)
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url=".?Whisper=true">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-2] Find Me with 3 Numbers and Fallback URL (from Example 2)
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url=".?Whisper=true">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-3] Find Me with Custom Timeout and Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
<Number url=".?Whisper=true&Message=Custom%20message">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-4] Allow up to 10 numbers
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
[FIND-ME-1-5] Discard Extra Numbers Above 10
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
[FIND-ME-1-6] No More Numbers, Without Fallback URL
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true" timeout="60">
<Number url=".?Whisper=true"/>
</Dial>
</Response>
[FIND-ME-1-7] No More Numbers, With Fallback URL
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3" timeout="60">
<Number url=".?Whisper=true"/>
</Dial>
</Response>
[FIND-ME-4-1] Call Answered
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
[FIND-ME-4-2] Call Completed
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
Checking differences with the output of the Find Me Twimlet,
$ ./test-find-me.sh "https://$DOMAIN.twil.io/findme" | diff twimlet-find-me.log -
4,5c4,5
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
> <Number url=".?Whisper=true">415-555-1212</Number>
12,13c12,13
< <Dial action="/findme?Dial=true&FailUrl=https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
> <Number url=".?Whisper=true">415-555-1212</Number>
20,21c20,21
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=42&Message=Custom+message&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
< <Number url="whisper?Message=Custom+message&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
> <Number url=".?Whisper=true&Message=Custom%20message">415-555-1212</Number>
28,29c28,29
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1001</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
> <Number url=".?Whisper=true">415-555-1001</Number>
36,37c36,37
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1011</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
> <Number url=".?Whisper=true">415-555-1001</Number>
44c44,46
< <Hangup/>
---
> <Dial action=".?Dial=true" timeout="60">
> <Number url=".?Whisper=true"/>
> </Dial>
48c50,55
< Redirect: https://example.com/please-try-later.mp3
---
> <?xml version="1.0" encoding="UTF-8"?>
> <Response>
> <Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3" timeout="60">
> <Number url=".?Whisper=true"/>
> </Dial>
> </Response>
we notice the following differences:
- parameters with empty values (
FailUrl=
,Message=
are omitted) - the relative URL
.?
is used to refer to the script itself - the flag
Whisper=true
is used to handle the Whisper stages internally instead of delegating to a separate script - space characters are encoded as
%20
instead of+
- the flag
HumanCheck=1
is omitted in the Whisper URL; this is the default value in the parameterMY_HUMAN_CHECK
in this script. TwiML<Redirect>
is used instead of an HTTP redirect
from function-templates.
I overlooked significant differences between the Twimlet and the Funlet on two cases:
44c44,46
< <Hangup/>
---
> <Dial action=".?Dial=true" timeout="60">
> <Number url=".?Whisper=true"/>
> </Dial>
and
48c50,55
< Redirect: https://example.com/please-try-later.mp3
---
> <?xml version="1.0" encoding="UTF-8"?>
> <Response>
> <Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3" timeout="60">
> <Number url=".?Whisper=true"/>
> </Dial>
> </Response>
from function-templates.
They correspond to:
[FIND-ME-1-6] No More Numbers, Without Fallback URL
Twimlet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
Funlet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true" timeout="60">
<Number url=".?Whisper=true"/>
</Dial>
</Response>
and
[FIND-ME-1-7] No More Numbers, With Fallback URL
Twimlet:
Redirect: https://example.com/please-try-later.mp3
Funlet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3
<Number url=".?Whisper=true"/>
</Dial>
</Response>
This is not expected.
from function-templates.
These two test cases were missing, and the corresponding part of the implementation was missing as well.
from function-templates.
I have added the missing test cases and implementation. Let's now check the integration tests again.
from function-templates.
Integration Tests
With https://$DOMAIN.twil.io/findme
the URL of a copy of the Find Me Funlet deployed in my Twilio account with checks for signed requests disabled:
$ ./test-find-me.sh "https://$DOMAIN.twil.io/findme"
[FIND-ME-1-1] Find Me with 3 Numbers (from Example 1)
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url=".?Whisper=true">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-2] Find Me with 3 Numbers and Fallback URL (from Example 2)
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
<Number url=".?Whisper=true">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-3] Find Me with Custom Timeout and Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
<Number url=".?Whisper=true&Message=Custom%20message">415-555-1212</Number>
</Dial>
</Response>
[FIND-ME-1-4] Allow up to 10 numbers
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
[FIND-ME-1-5] Discard Extra Numbers Above 10
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
[FIND-ME-1-6] No More Numbers, Without Fallback URL
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
[FIND-ME-1-7] No More Numbers, With Fallback URL
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Redirect>https://example.com/please-try-later.mp3</Redirect>
</Response>
[FIND-ME-4-1] Call Answered
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
[FIND-ME-4-2] Call Completed
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
Checking differences with the output of the Find Me Twimlet,
$ ./test-find-me.sh "https://$DOMAIN.twil.io/findme" | diff twimlet-find-me.log -
4,5c4,5
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
> <Number url=".?Whisper=true">415-555-1212</Number>
12,13c12,13
< <Dial action="/findme?Dial=true&FailUrl=https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&https%3A%2F%2Fexample.com%2Fplease-try-later.mp3&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="60">
> <Number url=".?Whisper=true">415-555-1212</Number>
20,21c20,21
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=42&Message=Custom+message&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
< <Number url="whisper?Message=Custom+message&HumanCheck=1">415-555-1212</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1313&PhoneNumbers%5B%5D=415-555-1414" timeout="42">
> <Number url=".?Whisper=true&Message=Custom%20message">415-555-1212</Number>
28,29c28,29
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1001</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
> <Number url=".?Whisper=true">415-555-1001</Number>
36,37c36,37
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1011</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
> <Number url=".?Whisper=true">415-555-1001</Number>
48c48,51
< Redirect: https://example.com/please-try-later.mp3
---
> <?xml version="1.0" encoding="UTF-8"?>
> <Response>
> <Redirect>https://example.com/please-try-later.mp3</Redirect>
> </Response>
we notice some expected differences:
- parameters with empty values (
FailUrl=
,Message=
are omitted) - the relative URL
.?
is used to refer to the script itself - the flag
Whisper=true
is used to handle the Whisper stages internally instead of delegating to a separate script - space characters are encoded as
%20
instead of+
- the flag
HumanCheck=1
is omitted in the Whisper URL; this is the default value in the parameterMY_HUMAN_CHECK
in this script. - TwiML
<Redirect>
is used instead of an HTTP redirect
as well as an uncommon one:
36,37c36,37
< <Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
< <Number url="whisper?Message=&HumanCheck=1">415-555-1011</Number>
---
> <Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
> <Number url=".?Whisper=true">415-555-1001</Number>
from function-templates.
It corresponds to the following test case:
[FIND-ME-1-5] Discard Extra Numbers Above 10
Twimlet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="/findme?Dial=true&FailUrl=&Timeout=&Message=&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
<Number url="whisper?Message=&HumanCheck=1">415-555-1011</Number>
</Dial>
</Response>
Funlet:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010&PhoneNumbers%5B%5D=415-555-1011&PhoneNumbers%5B%5D=415-555-1012" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
from function-templates.
The Twimlet has a bug identified previously: when more than 10 numbers are included, the first 10 are discarded and the following numbers kept, instead of keeping only the first 10 and discarding extra numbers.
The Funlet has a different bug: it does not discard the extra numbers, and happily keeps more than 10 numbers.
from function-templates.
I fixed that bug too, and extra numbers beyond the first 10 are now correctly discarded:
[FIND-ME-1-5] Discard Extra Numbers Above 10
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action=".?Dial=true&PhoneNumbers%5B%5D=415-555-1002&PhoneNumbers%5B%5D=415-555-1003&PhoneNumbers%5B%5D=415-555-1004&PhoneNumbers%5B%5D=415-555-1005&PhoneNumbers%5B%5D=415-555-1006&PhoneNumbers%5B%5D=415-555-1007&PhoneNumbers%5B%5D=415-555-1008&PhoneNumbers%5B%5D=415-555-1009&PhoneNumbers%5B%5D=415-555-1010" timeout="60">
<Number url=".?Whisper=true">415-555-1001</Number>
</Dial>
</Response>
from function-templates.
On the other hand, there are no unexpected differences in the two Whisper stages,
$ ./test-whisper.sh "https://$DOMAIN.twil.io/findme"
[WHISPER-2-1] Recorded Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Play>https://example.com/recorded-message.mp3</Play>
</Gather>
<Hangup/>
</Response>
[WHISPER-2-2] Text Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say language="en" voice="alice">Text message</Say>
</Gather>
<Hangup/>
</Response>
[WHISPER-2-3] Default Message
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say language="en" voice="alice">You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept.</Say>
</Gather>
<Hangup/>
</Response>
[WHISPER-2-4] Human Check
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say language="en" voice="alice">Text message</Say>
</Gather>
<Hangup/>
</Response>
[WHISPER-3-1] A Digit was Pressed
<?xml version="1.0" encoding="UTF-8"?>
<Response/>
[WHISPER-3-2] No Digits were Pressed
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Hangup/>
</Response>
We get the same differences as in the Call Me Funlet:
$ ./test-whisper.sh "https://$DOMAIN.twil.io/findme" | diff twimlet-whisper.log -
14c14
< <Say>Text message</Say>
---
> <Say language="en" voice="alice">Text message</Say>
23c23
< <Say>You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept.</Say>
---
> <Say language="en" voice="alice">You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept.</Say>
32c32
< <Say>Text message</Say>
---
> <Say language="en" voice="alice">Text message</Say>
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.