仕組みから理解するTwilio #3-2 - AWS API Gateway+Lambda実装Walkthrough(後編)

この記事は下記記事の続きです。先にこちらを読むことを推奨いたします。少なくともWalkthroughの前編を読まないと内容が把握できないと思います。
[仕組みから理解するTwilio #1 - はじめに](http://qiita.com/tmiki/items/ac28b6f6ad745f3e1b2e)
[仕組みから理解するTwilio #2 - 通信フロー・データ構造](http://qiita.com/tmiki/items/fad1a22ba1fe14d32e34)
[仕組みから理解するTwilio #3-1 - AWS API Gateway+Lambda実装Walkthrough(前編)](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa)


- 1. [はじめに](http://qiita.com/tmiki/items/ac28b6f6ad745f3e1b2e)
    - 1. Twilio利用時の基本構成・用語の定義
    - 2. 今回検証した環境
- 2. [通信フロー・データ構造](http://qiita.com/tmiki/items/fad1a22ba1fe14d32e34)
    - 1. 認証・CapabilityToken授受
    - 2. 外部電話からTwilioクライアントへのCall(IncomingCall)
    - 3. Twilioクライアントから外部電話へのCall(OutgoingCall)
- 3-1. [AWS API Gateway+Lambda実装Walkthrough(前編)](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa)
    - 1. API Serverの処理の実装(Python on AWS Lambda)
    - 2. API Gatewayの設定
- 3-2. **AWS API Gateway+Lambda実装Walkthrough(後編)**　**←いまココ**
    - **3. Twilio Clientの実装とデプロイ**
    - **4. 動作確認！**


# Walkthroughの概要
まずは、前編の[Walkthroughの概要](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa#walkthrough%E3%81%AE%E6%A6%82%E8%A6%81)を確認しましょう。

AWSに関する具体的な作業は下記のとおりとなります。

- 1. API Serverの処理の実装(Python on AWS Lambda)
    - 1-1. CapabilityToken取得APIを、Lambda関数として実装・デプロイする
    - 1-2. Incoming Call用TwiML返却APIを、Lambda関数として実装・デプロイする
    - 1-3. Outgoing Call用TwiML返却APIを、Lambda関数として実装・デプロイする
- 2. API Gatewayの設定
    - 2-1. AWS Management ConsoleのAmazon API Gatewayのメニューから、APIを作成する
    - 2-2. 各API用のResourceを定義する
    - 2-3. CORSを設定する
    - 2-4. CapabilityToken取得API(/twilio/retrieve_capability_token)用のMethodを定義する
    - 2-5. Incoming Call用TwiML返却API(/twilio/retrieve_twiml/call_incoming)用のMethodを定義する
    - 2-6. Outgoing Call用TwiML返却API(/twilio/retrieve_twiml/call_outgoing)用のMethodを定義する
    - 2-7. prodステージを作成しDeployする
    - 2-8. 各APIの動作確認を行う
- 3. Twilio Clientの実装とデプロイ
    - 3-1. Twilio ClientをJavaScriptで実装する
    - 3-2. S3 Bucketを作成し、Static Website Hostingを有効にする
    - 3-3. S3 Bucket上に配置する
- 4. 動作確認！
    - 4-1. WebブラウザでS3上のTwilio Clientに、HTTPSでアクセスする
    - 4-2. 検証済みの電話番号からTwilio Clientに電話をかける
    - 4-3. Twilio Clientから検証済みの電話番号に電話をかける


それでは続きの作業を進めていきましょう。

# 3. Twilio Clientの実装とデプロイ
HTML/JavaScriptで下記のようなTwilio Clientを実装します。
**なお、Twilio Clientを実行するPCはスピーカーとマイクが必要です。事前に買って繋げておきましょう。**
![twilio_client_webbrowser.png](https://qiita-image-store.s3.amazonaws.com/0/137777/b6a117cb-60a6-7fba-84cc-6db3b10c4a46.png)



## 3-1. Twilio ClientをJavaScriptで実装する
Twilioの公式ドキュメントにあるJavaScriptベースのクライアントをもとにカスタマイズします。

Twilio Client Javascript Quickstart
https://www.twilio.com/docs/quickstart/client/javascript

\# ただし、ベースとしたものは2015年8月頃のものです。twilio.jsのバージョンは1.2と古いものとなっております。
\# 2016/10/12現在、当時参考にしたチュートリアルが公式ドキュメント上に見当たりませんでした…

### ソースコード
今回実装したTwilio Clientは下記のとおりです。

```html:twilio_client.html
<!DOCTYPE html>
<html>
  <head>
    <title>Twilio client</title>
    <script type="text/javascript"
      src="https://static.twilio.com/libs/twiliojs/1.2/twilio.min.js"></script>
    <script type="text/javascript"
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
    </script>
    <link href="https://static0.twilio.com/resources/quickstart/client.css"
      type="text/css" rel="stylesheet" />
    <script type="text/javascript">

        // A Twilio phone number associated with this Twilio client.
        var twilioClientPhoneNumber = "+81-50-3123-4567";

        // Capability Token
        var capabilityToken = "";
        $.ajax({
            type: 'POST',
            url: 'https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/twilio/retrieve_capability_token',
            data: '{"twilioPhoneNumber":"' + twilioClientPhoneNumber + '"}',
            success: function(response){
                       console.log(response);
                       if (response.success == true){
                         capabilityToken = response.capabilityToken;
                       } else {
                         console.log("cannot retrieve a Capability Token.");
                       }
                     },
            dataType: 'json',
            async: false
        });

        /* Create a Client object with a Capability Token */
        Twilio.Device.setup(capabilityToken);

        Twilio.Device.ready(function (device) {
          $("#log").text("Ready");
        });

        Twilio.Device.error(function (error) {
          $("#log").text("Error: " + error.message);
        });
   
        Twilio.Device.connect(function (conn) {
          $("#log").text("Successfully established call");
        });
   
        Twilio.Device.disconnect(function (conn) {
          $("#log").text("Call ended");
        });
   
        Twilio.Device.incoming(function (conn) {
          $("#log").text("Incoming connection from " + conn.parameters.From);
          // accept the incoming connection and start two-way audio
          conn.accept();
        });
   
        $(function() {
          $('#capabilityToken').text(capabilityToken);
          $('#twilioClientPhoneNumber').text(twilioClientPhoneNumber);
        });

        /* Make a call to an external phone number entered by you. */
        function call() {
          params = {
            // Add custom parameters.  (This phone number should be described in E.164)
            // One is a phone number associated with this Twilio Client. Another is a phone number to connect
            "callOutgoingPhoneNumber": $("#number").val(),
            "callerPhoneNumber": twilioClientPhoneNumber
          };
          Twilio.Device.connect(params);
        }
   
        function hangup() {
          Twilio.Device.disconnectAll();
        }
    </script>
  </head>
  <body>
    <button class="call" onclick="call();">
      Call
    </button>
 
    <button class="hangup" onclick="hangup();">
      Hangup
    </button>
    <hr/>
    <h2>This Twilio client can run under HTTPS only, because it needs to use your PC's microphone device and invoke external REST APIs which provided by Twilio.</h2>
    <hr/>
 
  <div style="
      border: 1px solid gray;
      width: 80%;
      display: block;
      word-wrap: break-word;
      margin: 0 auto;
      padding: 10px;
    ">twilioClientPhoneNumber:<h3 id="twilioClientPhoneNumber"></h3></div>
  <div style="
      border: 1px solid gray;
      width: 80%;
      display: block;
      word-wrap: break-word;
      margin: 0 auto;
      padding: 10px;
    ">capabilityToken:<h3 id="capabilityToken"></h3></div>
 
    <input type="text" id="number" name="number"
      placeholder="Enter a phone number to call(e.g.+819012345678)"/>
 
    <div id="log">Loading pigeons...</div>
  </body>
</html>
```

環境に依存する箇所は下記のとおりとなります。それぞれご自身の環境に合わせて修正してください。

このTwilio Clientに紐付けられるTwilio電話番号を指定します。

```js
        // A Twilio phone number associated with this Twilio client.
        var twilioClientPhoneNumber = "+81-50-3123-4567";
```

API Gateway APIをDeployした際に確認したURLを指定します。
[2-7. prodステージを作成しDeployする](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa#2-7-prod%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97deploy%E3%81%99%E3%82%8B)で確認したCapabilityToken取得APIのInvoke URLを、$.ajax()関数のurlパラメータとして指定しましょう。

```js
        $.ajax({
            type: 'POST',
            url: 'https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/twilio/retrieve_capability_token',
            data: '{"twilioPhoneNumber":"' + twilioClientPhoneNumber + '"}',
            success: function(response){
                       console.log(response);
                       if (response.success == true){
                         capabilityToken = response.capabilityToken;
                       } else {
                         console.log("cannot retrieve a Capability Token.");
                       }
                     },
            dataType: 'json',
            async: false
        });
```

### 動作概要
Twilio Clientの動作を簡単に説明します。

- 初期処理
    - CapabilityTokenを取得します。これは、ソースコードに指定されたTwilio電話番号(twilioClientPhoneNumber)をもとに、CapabilityToken取得APIを実行します。
    - 習得したCapabilityTokenをもとにTwilio.Device.setup()関数を実行し、セットアップを行います。
    - Twilio.Deviceのセットアップが正常終了すると、画面下部に「Ready」と表示します。
- 電話を着信する
    - Twilio.Device.incoming()関数として登録された内容が実行されます。
    - Callボタンを押すことで、通話できるようになります。
- 電話をかける
    - 「Enter a phone number to call」と表示されているテキストボックスに電話番号を入力し「Call」を押します。
    - 実装したcall()関数が実行され、Twilio.Device.connect()が実行されます。この際、カスタムパラメータとして発信元Tilio電話番号と発信先電話番号が渡されます。


TwilioのJavaScriptライブラリの詳細は、下記公式ドキュメントを参照してください。

The twilio.js Library: Twilio.Device
https://www.twilio.com/docs/api/client/device

The twilio.js Library: Twilio.Connection
https://www.twilio.com/docs/api/client/connection


## 3-2. S3 Bucketを作成し、Static Website Hostingを有効にする
S3のStatic Website Hosting機能を利用し、実装したTwilio ClientをHTTPS上で動かせるようにします。
下記ドキュメントを参考にして設定しましょう。

Amazon S3 での静的ウェブサイトのホスティング
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/WebsiteHosting.html

ここでは、「twilio-on-aws」というBucketをTokyoリージョン(ap-northeast-1)に作成します。下記のURLでアクセスできるようになります。
https://twilio-on-aws.s3-ap-northeast-1.amazonaws.com/


## 3-3. S3 Bucket上に配置する
AWS Management Consoleからtwilio-on-aws Bucketに作成したソースコードをアップロードします。ここでは、twilio_client.htmlという名前でアップロードします。
アップロードが完了すると、下記のURLでアクセスできるようになります。
https://twilio-on-aws.s3-ap-northeast-1.amazonaws.com/twilio_client.html


# 4. 動作確認！
## 4-1. WebブラウザでS3上のTwilio Clientに、HTTPSでアクセスする
Webブラウザで下記のURLを開きます。
https://twilio-on-aws.s3-ap-northeast-1.amazonaws.com/twilio_client.html

画面下部のステータス表示領域に「Ready」と表示され、「capabilityToken:」にCapabilityTokenが表示されることを確認しましょう。
また、Twilioクライアントはマイクの利用が必要ですのでこれを許可しておきます。


## 4-2. 検証済みの電話番号からTwilio Clientに電話をかける
お手持ちの携帯電話等から、Twilio電話番号に電話をかけてみましょう。
TwilioアカウントがTrial Accountの場合は、検証済みの番号からのみかけることができます。下記URLから、ご自身の電話を検証済みにしておきましょう。
https://jp.twilio.com/console/phone-numbers/verified

Twilio Clientは自動的に着信し、通話が始まります。
マイクが接続されていないか、マイクの利用が許可されていない場合、ステータス表示領域に下記のエラーが表示され、着信できません。

```
Error: User denied access to microphone, or the web browser did not allow microphone access at this address.
```

なお、Trial Accountの場合は 「Twilioをご利用いただきありがとうございます。 アップグレードいただきますとこのメッセージは削除されます。 いずれかのダイヤルキーを押してください。」 というアナウンスが流れます。流れた後に適当な数字キーを押す必要があります。

通話を切る場合は「Hangup」ボタンを押します。


## 4-3. Twilio Clientから検証済みの電話番号に電話をかける
Twilio Clientからご自身の電話に電話をかけてみましょう。
「Enter a phone number to call」と表示されているテキストボックスに電話番号を入力し「Call」を押します。接続が開始されるとステータス表示領域に「Successfully established call」と表示されます。

しばらくすると、ご自身の電話に着信しますので、通話することができます。

なお、Trial Accountの場合は 「Twilioをご利用いただきありがとうございます。 アップグレードいただきますとこのメッセージは削除されます。」といったアナウンスが流れた後に発信が開始されます。

通話を切る場合は「Hangup」ボタンを押します。