I wrote about the reception system, but what I want to do is basically an extension of the following quick start.
PYTHON Quickstart: Make a call from your browser
When I wondered if I could do something with WebRTC, a Twilio client, I thought I could implement a serverless reception phone, which is common in the company, using a Web browser such as a tablet.
Twilio officially has a sample code for browser calls using Flask, but the information on twilio-python and twilio.js is a little old, so it didn't work as it was, so it works with the latest code. I'm trying to do it.
Instead of using an extension phone, you can make a call directly to your personal smartphone. The advantage is that you don't need a landline phone and you don't have to give out your personal phone number. Also, since it is implemented without a server, it is maintenance-free and inexpensive. (Call charges will be incurred ...)
This time, I wanted to create an environment of AWS Lambda + API GateWay quickly, so I used AWS Chalice.
$ pip install chalice
Next, set up your AWS credentials. If you have already set it using boto3 etc., you probably don't need it.
$ mkdir ~/.aws
$ vim ~/.aws/credentials
[default]
aws_access_key_id=YOUR_ACCESS_KEY_HERE
aws_secret_access_key=YOUR_SECRET_ACCESS_KEY
region=YOUR_REGION (such as us-west-2, us-west-1, etc)
Create a project in Chalice.
$ chalice new-project EmployeeCaller
$ cd EmployeeCaller
When you execute the above command, a file will be created under the directory, so edit that file.
First, write the library to deploy in requirements.txt.
$ vim requirements.txt
twilio==6.0.0
Then edit app.py and put in the code.
app.py
import os, json
from urlparse import parse_qs
from chalice import Chalice, Response
from twilio.jwt.client import ClientCapabilityToken
from twilio.twiml.voice_response import VoiceResponse
app = Chalice(app_name='EmployeeCaller')
#Generate TwiML, the endpoint of voice calls
@app.route('/voice', methods=['POST'], content_types=['application/x-www-form-urlencoded'], cors=True)
def voice():
parsed = parse_qs(app.current_request.raw_body)
dest_number = parsed.get('PhoneNumber', [])
resp = VoiceResponse()
resp.dial(dest_number[0], caller_id=os.environ['TWILIO_CALLER_ID'])
return Response(body=str(resp), status_code=200, headers={'Content-Type': 'application/xml'})
#Issue a token
@app.route('/client', methods=['GET'])
def client():
request = app.current_request
account_sid = os.environ['TWILIO_ACCOUNT_SID']
auth_token = os.environ['TWILIO_AUTH_TOKEN']
application_sid = os.environ['TWILIO_TWIML_APP_SID']
capability = ClientCapabilityToken(account_sid, auth_token)
capability.allow_client_outgoing(application_sid)
capability.allow_client_incoming(os.environ['DEFAULT_CLIENT'])
token = capability.to_jwt()
callback = request.query_params['callback']
return str(callback) + "(" + json.dumps({"token": token}) + ")"
A little supplement to Chalice, Chalice is based on a micro-framework called Flask. So I think you should look at Flask's documentation for routing etc.
First of all, regarding the following routing
@app.route('/client', methods=['GET'])
You'll need to get a Capability Token issued to make a call on your Twilio client. Hit / client
to generate this token and return it with json. It feels like the front side uses this to make a call.
You can read more about tokens in TWILIO Client Capability Tokens.
Then the following routing
@app.route('/voice', methods=['POST'], content_types=['application/x-www-form-urlencoded'], cors=True)
This / voice
will receive a Request from the Twilio server that includes the phone number of the connection destination. Since it is sent by POST, you need to specify ʻapplication / x-www-form-urlencoded` for Content-Type.
The part of os.environ ['---'] in the code is the place to read the environment variable, which is written in the Chalice configuration file, but it is set after deploying once and setting Twilio.
Deploy the above code. Execute the following command in the project directory.
$ chalice deploy
Updating IAM policy.
Updating lambda function...
Regen deployment package...
Sending changes to lambda.
API Gateway rest API already found.
Deploying to: dev
https://********.execute-api.ap-northeast-1.amazonaws.com/dev/
When you run it, you should see a result like the one above. Make a note of the URL that was output last, as it will be used later as the URL to call APIGateWay.
Log in to Twilio and make a note of the Console Dash Board's "ACCOUNT SID" and "AUTH TOKEN".
Create a new TwiML APP from Phone Number> Tools. Enter the App name as appropriate for the friendly name.
Enter the URL + / voice
that you wrote down when you did chalice deploy
earlier in "Request URL" of "Voice call" and save it. (https: //********.execute-api.ap-northeast-1.amazonaws.com/dev/voice
)
When you create a TwiML APP, the App will appear in the list, so display the details, check the "SID" (TwiML App SID), and make a note of it.
Purchase a phone number from "Phone Number" with "Buy a Number". Make a note of your phone number.
How to purchase a phone number [previously written article](http://qiita.com/hidesakai/items/20873fa354cb49911608#twilio%E3%81%A7%E9%9B%BB%E8%A9%B1%E7%95 % AA% E5% 8F% B7% E8% B3% BC% E5% 85% A5% E8% AA% B2% E9% 87% 91), so if you don't know, please refer to that.
After creating the TwiML APP, go back to your chalice project and edit the following files.
$ vim .chalice/config.json
Add an item called ʻenviroment_variables` to the config.json file. Set the SID and Token you wrote down earlier here. This will be an environment variable on AWS Lambda.
.....
"app_name": "EmployeeCaller",
//Added the following items
"environment_variables": {
"TWILIO_ACCOUNT_SID": "*******************",
"TWILIO_AUTH_TOKEN": "*******************",
"TWILIO_TWIML_APP_SID": "*******************",
"TWILIO_CALLER_ID": "+81********",
"DEFAULT_CLIENT": "reception"
}
After editing config.json, deploy again.
$ chalice deploy
Next, create a reception screen.
$ vim employee.html
employee_call.html
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://media.twiliocdn.com/sdk/js/client/v1.4/twilio.min.js"></script>
<script type="text/javascript">
Twilio.Device.ready(function (device) {
console.log("Ready");
});
Twilio.Device.error(function (error) {
console.log("Error: " + error.message);
});
Twilio.Device.connect(function (conn) {
console.log("Successfully established call");
});
Twilio.Device.disconnect(function (conn) {
console.log("Call ended");
$('.employee-hangup').addClass('disabled').prop('disabled', true);
$('.employee-call').removeClass('disabled').prop('disabled', false);
});
Twilio.Device.incoming(function (conn) {
console.log("Incoming connection from " + conn.parameters.From);
conn.accept();
});
function twilioReadyAsync(phoneNumber) {
return new Promise(function(resolve){
(function ready(){
if (Twilio.Device.status() == 'ready') {
resolve({"PhoneNumber": phoneNumber});
}
setTimeout(ready, 1000);
})();
});
}
$(function() {
$('.employee-hangup').addClass('disabled').prop('disabled', true);
$('.employee-call').click(function(){
var employeePhoneNumber = $(this).attr('data-phone-number');
$(this).next().removeClass('disabled').prop('disabled', false);
$('.employee-call').addClass('disabled').prop('disabled', true);
$.ajax({
url: 'https://******.execute-api.ap-northeast-1.amazonaws.com/dev/client',
dataType: 'jsonp',
jsonCallback: 'callback'
})
.done(function(data) {
Twilio.Device.setup(data.token);
twilioReadyAsync(employeePhoneNumber).then(Twilio.Device.connect);
});
});
$('.employee-hangup').click(function(){
Twilio.Device.disconnectAll();
$(this).addClass('disabled').prop('disabled', true);
$('.employee-call').removeClass('disabled').prop('disabled', false);
});
});
</script>
<style>
.container {width: auto;}
</style>
</head>
<body>
<div class="container">
<h1>Reception</h1>
<div class="card-deck">
<div class="card text-center" id="employee-1">
<img class="card-img-top img-fluid" src="hidesakai.png " alt="Card image cap">
<div class="card-block">
<h4 class="card-title">hidesakai</h4>
<p class="card-text">
<p>Development: Engineer</p>
Please contact us by extension when you need it.
</p>
<button class="btn btn-primary employee-call" data-phone-number="+8190-****-****">Call</button>
<button class="btn btn-danger employee-hangup">Hangup</button>
</div>
</div>
<div class="card text-center" id="employee-2">
<img class="card-img-top img-fluid" src="spam.jpg " alt="Card image cap">
<div class="card-block">
<h4 class="card-title">Spam</h4>
<p class="card-text">
<p>Design: Designer</p>
Please contact us from the reception.
</p>
<button class="btn btn-primary employee-call" data-phone-number="+8190-****-****">Call</button>
<button class="btn btn-danger employee-hangup">Hangup</button>
</div>
</div>
<div class="card text-center" id="employee-3">
<img class="card-img-top img-fluid" src="egg.png " alt="Card image cap">
<div class="card-block">
<h4 class="card-title">Egg</h4>
<p class="card-text">
<p>Sales: Marketer</p>
</p>
<button class="btn btn-primary employee-call" data-phone-number="+8190-****-****">Call</button>
<button class="btn btn-danger employee-hangup">Hangup</button>
</div>
</div>
<div class="card text-center" id="employee-4">
<div class="card-block">
<h4 class="card-title">Reception</h4>
<p class="card-text">
This is the reception.
</p>
<button class="btn btn-primary employee-call" data-phone-number="+8180-****-****">Call</button>
<button class="btn btn-danger employee-hangup">Hangup</button>
</div>
</div>
</div>
</div>
</body>
</html>
$ .ajax ()
ʻurl:'https: //******.execute-api.ap-northeast-1.amazonaws.com/dev/client'` URL when charis deployed earlier Is added as appropriate.
Then enter the phone number starting with the country code (+81) in data-phone-number
.
Create a suitable bucket on S3 and upload the html file.
After uploading to S3, go to the page and press the Call button. It is OK if you can send to the set phone number.
At this rate, you can access it from anywhere and you can play tricks on it.
After that, I have put the phone number directly on the html, but I would like to get the data from Lambda such as DynamoDB.
Recommended Posts