仕組みから理解するTwilio #1 - はじめに 

# はじめに
手軽且つ汎用的なテレフォニーサービスとしてTwilioが各所で使われており、諸兄諸姉も様々なプロジェクトでご利用のことと存じます。

公式ドキュメントには各言語のサンプルコードは豊富にありますが、飽くまで各言語からの呼び出し方法が主眼となっており、通信プロトコル・データ構造についての情報がありませんでした。

特に、外部の電話からTwilioクライアントに対して電話をかけ通話する方法、TwiMLを返却するサーバの実装について、詳しい情報を見つけることができませんでした。
\# 断片的な情報はありましたが、これを実用的なレベルに組み合わせるのに苦労しました…

そこで、Twilioの仕組みがどうなっているかという観点を意識し、実際に検証した結果やパケットキャプチャした結果等を突き合わせながら、必要な情報をまとめたいと思います。

公式ドキュメントに記載がない情報は、飽くまで現時点で検証したものであり、将来的に変更となる可能性がありますのでご注意ください。

# このテーマの構成
このテーマは分量が多いため、大きく以下の4セクションに分割します。
この記事は「1. はじめに」に相当します。

- 1. **はじめに　←いまココ**
    - 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(後編)](http://qiita.com/tmiki/items/31671548bd755606625a)
    - 3. Twilio Clientの実装とデプロイ
    - 4. 動作確認！


## 想定する読者
本記事および関連記事は、下記の読者を想定しています。

- Twilioアカウントを作成済みであること（Trialバージョンで可）
- Twilio公式ドキュメントにあるTutorialを軽く読み流したことがあること（実際に試していれば尚可）
- AWSアカウントを作成済みであること
- AWSを利用したことがあること


# Twilio利用時の基本構成・用語の定義
ここからが本題となります。まずはTwilioが想定する基本的な構成・通信フローを確認します。

下図は、公式ドキュメントからの引用となりますが、「Architecture」に記載の図は非常に重要なので押さえておく必要があります。
これはAndroidデバイスからTwilioを利用して電話をかける／受ける構成ですが、典型的にはこのような構成となることが殆どです。

![twilio-android-diagram.original.png](https://qiita-image-store.s3.amazonaws.com/0/137777/00474859-09c8-538e-9264-129b5e48ed45.png)
Twilio Client Android SDK - Architecture
https://www.twilio.com/docs/api/client/android

主要な登場人物は主に下記の3つです。
それぞれ正式名称は無いようですが、本記事では下記の3つの呼称で統一します。

- Twilio Client
    - もともと電話番号を持たないデバイスで、AndroidアプリやJavaScriptアプリがこれにあたります。
- API Server
    - 自前で用意するサーバです。図中で「your server」となっているものです。
    - まず、Twilio Clientの認証に必要です。
    - また、電話アプリの挙動を定義するTwiML（※後述）を提供するために必要です。
    - 下記のTwilio ServerからHTTPリクエストを受け、TwiMLを返却します。
    - そのため、Internet上に公開されている必要があります。
- Twilio Server
    - Twilio側で運用されているサーバです。詳細は不明です。
    - Twilio Clientからの通信を仲介したり、必要に応じてAPI ServerにTwiML取得リクエストを行ったり、多岐の処理を行います。
    - Twilio Clientの認証・初期セットアップの一端を担います。
    - 認証成功時、当該Twilio Clientが接続可能になったという情報も保持されます（※推測。未検証。）。この段階で、論理的には他のレガシー電話からも通話可能な状態になります。（本当に通話できるかどうかは、TwiML次第ですが）

なお、電話をかける方向を説明する際には、できる限りTwilio Clientを中心に据えます。
つまり、Twilio Clientから電話をかける／Twilio Clientで電話を受ける、といった表現をします。
\# ちなみにTwilioのSDKやAPIで利用できる機能には、incomingやoutgoingなどが付与されたものがありますが、これらも全てTwilio Clientを中心に据えています。

その他、本記事及び一連の関連記事内で扱う用語を下記のように定義します。

| No | 用語 | 内容 |
|:-:|:--|:--|
| 1 | Twilio電話番号 | Twilioから購入し、Twilio Clientに紐付けられる電話番号を呼びます。常にE.164形式で表現されます。 |
| 2 | E.164形式 | 「+」から始まり、国番号・電話番号が続く形式です。途中にハイフンや空白を入れることができます。e.g. +81-3-1234-5678 |
| 3 | TwiML | ※公式ドキュメントを参照してください(https://www.twilio.com/docs/api/twiml) |
| 4 | Client Name | Twilio Clientに紐付けられるUniqueな名前。Twilio Clientで電話を受けるためには必須。 |
| 5 | Incoming Call | 外部の電話からTwilio Clientへ電話をかけること。 インバウンド通話と書くこともあります。|
| 6 | Outgoing Call | Twilio Clientから外部へ電話をかけること。 アウトバウンド通話と書くこともあります。|


# 今回検証した環境
今回実現するTwilio Clientは、まずは通常の電話と同じことができることを目指しました。
具体的な仕様は以下となります。

- 出来ること
    - Twilio Clientから、任意の番号を指定して一般の電話にかけることができる（アウトバウンド通話）
    - 一般の電話からTwilio Clientに電話をかけることができる（インバウンド通話）
- Twilio Clientの仕様
    - 自身を識別する値として、Twilio電話番号のみ保持する。自身のClient Nameは保持しない。
    - HTML/JavaScriptで実装され、S3上に配置される。

アーキテクチャは下記のような構成となりました。

![TwilioDiagrams_Fig1-1.png](https://qiita-image-store.s3.amazonaws.com/0/137777/e15475fa-d617-e0cc-4c2f-122ab088d8e4.png)

自前で用意するAPI ServerはAWSを利用しています。API GatewayとLambdaを利用し、Pythonで実装しました。リージョンはTokyo(ap-northeast-1)です。
Twilio Clientは、TwilioのJavaScriptチュートリアルをベースに改修しました。これをS3上に配置し、Webブラウザからアクセスします。

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

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


API Serverが提供するAPIを下記の通り定義しました。

| Endpoint | 説明 | 一連の記事内での呼称 |
|:--|:--|:--|
| /twilio/retrieve_capability_token | Twilio ClientにCapability Tokenを返却します。 | CapabilityToken取得API |
| /twilio/retrieve_twiml/call_incoming | Twilio Clientに電話をかける際に利用されるTwiMLを返却します。 | Incoming Call用TwiML返却API |
| /twilio/retrieve_twiml/call_outgoing | Twilio Clientから電話をかける際に利用されるTwiMLを返却します。 | Outgoing Call用TwiML返却API |

API Gatewayのprodステージにdeployしています。したがって、完全なURLは下記のようになります。
URLの構成はAPI Gatewayでの実装の際に説明しますのでここでは割愛します。

https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/twilio/retrieve_capability_token
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/twilio/retrieve_twiml/call_incoming
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/twilio/retrieve_twiml/call_outgoing


## Twilioアカウントの開設・準備
日本のTwilioサイトからアカウントを登録します。米国のサイトから登録しないように注意してください。
http://twilio.kddi-web.com/


### Twilio電話番号の取得・設定
電話番号を取得し、必要な設定を行います。Twilio Consoleにログイン後、左のメニューの「電話番号」から新たな番号を取得します。Trialアカウントでも1つは取得できます。
ただし、実際の電話と通話するためには「検証」が必要です。
「電話番号」→「検証済み電話番号」から、通話をテストするための電話番号を登録します。

電話番号を取得後、「A CALL COMES IN」のURLを設定します。ここにはIncoming Call用TwiML返却APIのURLを設定します。（実際のURLは[3. AWS API Gateway+Lambda実装Walkthrough](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa)で構築するものとなります。）
下図を参考にしてください。
これは、実際の電話からこのTwilio電話番号に電話をかける際に利用されます。
![2016-10-16_03h52_20.png](https://qiita-image-store.s3.amazonaws.com/0/137777/ad2a148e-c842-a5f7-254f-1462293bdd3e.png)


### TwiML Appの作成・設定
TwiML Appを作成し、必要な設定を行います。
Twilio Consoleにログイン後、左のメニューの「プログラマブルVoice」→「ツール」→「TwiML Apps」と遷移します。ここから新しいTwiML Appを作成できます。

「音声通話」の「REQUEST URL」を設定します。ここにはOutgoing Call用TwiML返却APIのURLを設定します。（実際のURLは[3. AWS API Gateway+Lambda実装Walkthrough](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa)で構築するものとなります。）
下図を参考にしてください。
![2016-10-16_05h14_56.png](https://qiita-image-store.s3.amazonaws.com/0/137777/3f69edaa-d85f-449e-7dd9-d4053adcb904.png)

## AWSアカウントの開設・実装
AWSアカウントを開設します。
具体的な実装は[3. AWS API Gateway+Lambda実装Walkthrough](http://qiita.com/tmiki/items/7f55a81c46dd07b2e4fa)で説明しますので、ここでは割愛します。

# まとめ
ここまで、本テーマの概要と準備する事項について説明しました。
続きは下記の記事になります。

[仕組みから理解するTwilio #2 - 通信フロー・データ構造](http://qiita.com/tmiki/items/fad1a22ba1fe14d32e34)