この記事は下記記事の続きです。一連の記事で独自に定義している用語等がありますので、先にこちらを読むことを推奨いたします。
仕組みから理解するTwilio #1 - はじめに
ここでは、Twilioを利用する際の通信フローや、授受されるデータ構造について説明します。
-
-
- Twilio利用時の基本構成・用語の定義
-
- 今回検証した環境
-
-
- 通信フロー・データ構造 ←いまココ
- 1. 認証・CapabilityToken授受
- 2. 外部電話からTwilioクライアントへのCall(IncomingCall)
- 3. Twilioクライアントから外部電話へのCall(OutgoingCall)
- 3-1. AWS API Gateway+Lambda実装Walkthrough(前編)
-
- API Serverの処理の実装(Python on AWS Lambda)
-
- API Gatewayの設定
-
- 3-2. AWS API Gateway+Lambda実装Walkthrough(後編)
-
- Twilio Clientの実装とデプロイ
-
- 動作確認!
-
今回実現するTwilio Clientは、まずは通常の電話と同じことができることを目指しました。
具体的な仕様は以下となります。
- 出来ること
- Twilio Clientから、任意の番号を指定して一般の電話にかけることができる(アウトバウンド通話)
- 一般の電話からTwilio Clientに電話をかけることができる(インバウンド通話)
- Twilio Clientの仕様
- 自身を識別する値として、Twilio電話番号のみ保持する。自身のClient Nameは保持しない。
- HTML/JavaScriptで実装され、S3上に配置される。
電話番号等のパラメータは下記であると仮定します。
パラメータ | 値 | 説明・備考 |
---|---|---|
Twilio電話番号 | 050-3123-4567 | Twilio Clientに割り当てた電話番号。Twilioコンソールから事前に購入しておいて下さい。 |
Client Name | DeviceId_0001 | Twilio Server内で当該Twilio Clientを識別・制御するための名前。 |
外部の電話 | 090-5987-6543 | Twilio外の電話。ご自身の携帯電話等を利用してください。 |
認証・CapabilityToken授受
処理フロー
処理のフローは下記のようになります。
- 1-1. Capability Tokenの取得リクエスト
- Twilio ClientはAPI Serverに対し、Capability Tokenを取得するためのリクエストを行います。
- API ServerはTwilio Clientから送信された情報をもとに、必要に応じて認証・認可を行います。
- 1-2. Capability Tokenの返却
- API Serverは、Capability Tokenを生成し、Twilio Clientに返却します。
- この際、当該Capability Tokenを利用して何ができるか(電話をかけることができる/受けることができる)、Twilio ClientのClient Name、有効期限といった情報が付与されます。
- 1-3.Twilio Clientのセットアップ
- Twilio ClientはCapability Tokenの受け取りに成功した後、必要なセットアップ処理を行います。
- JavaScriptであれば、Twilio.Device.setupを実行します。(https://www.twilio.com/docs/api/client/device#method-reference)
- Androidであれば、Twilio#createDeviceを実行し、Deviceオブジェクトを生成します。(https://jp.twilio.com/docs/api/client/android/Twilio#createDevice_String__DeviceListener_)
- セットアップ処理の実行の際、Capability TokenがTwilio Serverに渡されるようです。Twilio ServerにてCapability Tokenを検証し、その成否をTwilio Clientに返すようです。(※詳細は未確認)
1-1及び1-2の間の通信方式の実装は、完全にユーザに任されています。通信にHTTP/HTTPS及びそれ以外のプロトコルを使うことも可能です。
データ構造も完全に任意です。
Twilio Clientから送信する情報には、Twilio Clientを識別するための情報として、ユーザー名やパスワードやその他情報を利用することができます。任意の認証・認可を実現することも可能です。
API Serverから返却される情報としてCapability Tokenは必須ですが、他にも任意の情報を返却することが可能です。
今回の検証では、リクエスト・レスポンスともJSONで実装しました。具体的なデータ構造は下記のとおりです。
リクエストはシンプルに、Twilio電話番号のみとしました。指定されたTwilio電話番号が取得済みのものであれば認証成功とします。
{"twilioPhoneNumber": "+81-50-1234-9876"}
レスポンスは、Capability Token及び処理の成否としました。
{"capabilityToken": capabilityToken, "success": True}
1-3の通信については未検証です。詳細は不明です。JavaScriptクライアントの場合はWebSocketを利用し、CapabilityToken等の情報をTwilio Serverに送信しているようです。
Twilioが提供する各種SDKから初期セットアップが行われるため、通常はユーザーが意識する必要はありません。
# ただし、トラブルシュート時にはこの情報が必要になるケースはあり得ると思いますが。
Capability Tokenの作成・データ構造
API Serverでは、TwilioのHelperライブラリを利用してCapability Tokenを生成します。
Python2.7での実装をもとに説明します。必要なライブラリ類は既にインストール済みで、利用できるものとします。
https://www.twilio.com/docs/libraries/python#help
また、Twilioアカウントを開設済みでありAccount SID及びAuth Tokenは確認済みであること、TwiML Appを作成しApp SIDを確認済みであることとします。
from twilio.util import TwilioCapability
import json
def generate_capability_token(twilio_phone_number):
# 必要なパラメータを設定します。
twilio_account_sid = "{{twilio_accound_sid}}" # TwilioアカウントのSIDを指定します。
twilio_auth_token = "{{twilio_account_auth_token}}" # Twilioアカウントに紐付くAuth Tokenを指定します。
twilio_app_sid = "{{twilio_app_sid}}" # 事前に作成したApp Sidを指定します。
expiration_time_for_capability_token = 3600 # Capability Tokenの有効期限を指定します。
# Capability Tokenを生成し、必要な権限を付与します。
capability = TwilioCapability(twilio_account_sid, twilio_auth_token)
capability.allow_client_incoming(get_client_name_by_phone_number(twilio_phone_number)) # 指定されたTwilio電話番号をもとに、Twilio Client Nameを取得します。get_client_name_by_phone_numberは自分で個別に実装した関数です。
capability.allow_client_outgoing(twilio_app_sid)
capabilityToken = capability.generate(expiration_time_for_capability_token)
# 既定の構造に変換し、返却します。
res = {"capabilityToken": capabilityToken, "success": True}
return json.dumps(res)
基本的には下記ドキュメントに従った実装となります。
Generate Capability Tokens
http://twilio-python.readthedocs.io/en/latest/usage/token-generation.html
TwilioアカウントSIDとAuth TokenをもとにTwilioCapabilityを生成した後、allow_client_incoming及びallow_client_outgoingメソッドで権限を付与していきます。
最後にTwilioCapabilityのgenerateメソッドを実行することで、Capability Tokenを生成することができます。引数には有効期限を秒単位で指定することができます。
Capability Tokenの詳細は下記ドキュメントにあります。
Twilio Client: Capability Tokens
https://www.twilio.com/docs/api/client/capability-tokens
Capbility TokenはJWT(Json Web Token)形式です。生成の際にはTwilio Serverとの通信は必要としません。
未検証ですが、恐らく必要な情報をJSON化し、Auth Tokenで電子署名を生成して付与したのち、Base64デコードしているものと推測されます。
ここで重要なポイントは下記のとおりです。
- Twilio Clientから電話をかけるための権限と、Twilio Clientで電話を受けるための権限が違うこと
- Twilio Clientから電話をかけるための権限は、TwiML appのApp sidで指定すること
- Twilio Clientで電話を受けるための権限は、Client Nameで指定すること
次から、Twilio Clientと他の電話との通話の仕組みを見ていきます。
外部電話からTwilioクライアントへのCall(IncomingCall)
処理フロー
また、Twilioアカウントをセットアップし電話番号を取得した際に設定したURLを思い出してください。
「A CALL COMES IN」で指定したURLは、電話を受ける際に利用されます。
処理のフローは下記のようになります。TwiMLの取得は受信先のTwilio電話番号に設定されたURLが利用されます。
- 2-1. 実際の電話からTwilio Clientへ電話をかける
- 実際の電話から、Twilio Clientに紐付けられたTwilio電話番号に対し、電話をかけます。
- 当然ですが、電話をかける際にTwilio ClientのClient Nameを指定することはできません。
- 2-2. Twilio ServerからAPI ServerへのTwiML取得リクエスト
- Twilio Serverは、call対象となった電話番号の「A CALL COMES IN」に指定された通りにHTTPリクエストを発行します。
- 渡されるパラメータとして重要なものは、発信元の電話番号と受信先のTwilio電話番号です。
- 2-3. API ServerからTwilio ServerへTwiMLを返却する
- API Serverは、パラメータとして渡されたTwilio電話番号をもとに、これに紐付くClient Nameを取得する必要があります。
- Client Nameが取得でき次第、TwiMLを生成して返却します。
- 2-4. Twilio ServerはTwiMLに書かれた内容を実行する。
- TwiMLには、指定されたClient Nameに電話をつなげるよう指示があります。
- 発信元の電話をTwilio Clientにつなぎます。
API Serverから返却するべきTwiML
項番2-3で、API Serverから返却されるTwiMLは下記のようになります。
このTwiMLは、指定されたTwilio Clientに電話をかけることを意味しています。
<?xml version="1.0\" encoding="UTF-8"?>
<Response>
<Dial timeout="60">
<Client>DeviceId_0001</Client>
</Dial>
</Response>
<Client/>エレメントには、Twilio電話番号そのものを指定することができません。
Twilio電話番号に紐付くTwilio Clientの、Client Nameを入れる必要があります。
# ここでは仮に「DeviceId_0001」としています。
従ってAPI Serverは、項番2-3において、受信先のTwilio電話番号情報をもとに、Client Nameを取得する必要があります。予めDBなどに紐付け情報を保存しておき、TwiML生成時に取得するのが良いでしょう。
なお、TwiMLでは様々な制御を行うことが可能です。詳細は公式ドキュメントを参照してください。
https://www.twilio.com/docs/api/twiml/dial
https://www.twilio.com/docs/api/twiml/client
Twilio ServerからのHTTP POSTリクエスト
Twilio ServerからAPI Serverへのリクエストは少し特殊です。
また、公式ドキュメントには詳しい情報が見当たりませんでした。以下はパケットキャプチャした情報をもとに説明します。
上図はTwilio ServerからのHTTP POSTリクエストをパケットキャプチャしたものです。
ポイントは下記のとおりです。
- Content-Type:はapplication/x-www-form-urlencodedであること
- パラメータは、HTTPリクエストBodyに「&」で連結されたKey Valueペアで、1行で指定されること(GETパラメータのような形式)
- HTTPリクエストBody内の各パラメータはURLエンコードされていること
各パラメータの詳細を見ていきましょう。
発信元の電話番号を「090-5987-6543」、受信先のTwilio電話番号を「050-3123-4567」としています。
パラメータ | 説明(推測ベース) |
---|---|
AccountSid=AC3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | TwilioアカウントのSID。 |
ApiVersion=2010-04-01 | TwilioのAPIのバージョンか? |
Called=%2B815031234567 | 受信先のTwilio電話番号。E.164形式。Toと同一の値が入る模様。 |
CalledCity= | |
CalledCountry=JP | |
CalledState= | |
CalledVia=05031234567 | 不明。Twilio内で電話転送された場合に利用されるものか? |
CalledZip= | |
Caller=%2B819059876543 | 発信元の電話の電話番号。Fromと同一の値が入る模様。 |
CallerCity= | |
CallerCountry=JP | |
CallerState= | |
CallerZip= | |
CallSid=CA86nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn | 当該通話に付与されるUniqueなIDか? |
CallStatus=ringing | |
Direction=inbound | 通話の方向か? |
ForwardedFrom=05031234567 | 不明。Twilio内で電話転送された場合に利用されるものか? |
From=%2B819059876543 | 発信元の電話の電話番号。E.164形式。Callerと同一の値が入る模様。 |
FromCity= | |
FromCountry=JP | |
FromState= | |
FromZip= | |
To=%2B815031234567 | 受信先のTwilio電話番号。Calledと同一の値が入る模様。 |
ToCity= | |
ToCountry=JP | |
ToState= | |
ToZip= |
ここで重要なパラメータは、Caller/From/Called/Toの4つと考えられます。
API Server内で、この情報をもとにTwiMLを生成します。
PythonのQuick Startアプリは、発信元の情報としてCallerを、受信先の情報としてToを利用しているようです。そのためIncoming Call用TwiML返却APIの実装も、このパラメータを利用するようにします。
Twilioクライアントから外部電話へのCall(OutgoingCall)
処理フロー
また、Twilioアカウントをセットアップし、TwiML Appを作成した際に設定したURLを思い出してください。
処理のフローは下記のようになります。TwiMLの取得はTwiML Appに設定されたURLが利用されます。
- 3-1. Twilio Clientから発信リクエストを行う
- Twilio Clientから、発信するためのリクエストを行います。
- このとき、Twilio Clientから任意のパラメータを付与することができます。
- Androidであれば、セットアップ時に生成されたDeviceオブジェクトのconnectメソッドを実行します。(https://www.twilio.com/docs/api/client/android/device#connect_java_util_Map__ConnectionListener_)
- JavaScriptであれば、Twilio.Device.connectメソッドを実行します。(https://www.twilio.com/docs/api/client/device#connect)
- 3-2. Twilio ServerからAPI ServerへのTwiML取得リクエスト
- Twilio Serverは、CapabilityTokenに紐付けられたTwiML Appの、「REQUEST URL」に指定された通りにHTTPリクエストを発行します。
- 渡されるパラメータとして重要なものは、Twilio ClientのClient Name、発信先電話番号、Twilio Clientから渡されたパラメータです。
- 3-3. API ServerからTwilio ServerへTwiMLを返却する。
- API Serverは、パラメータとして渡された情報をもとに、発信元となるTwilio電話番号、発信先の電話番号を取得する必要があります。
- 必要な情報を取得でき次第、TwiMLを生成して返却します。
- 3-4. Twilio ServerはTwiMLに書かれた内容を実行する。
- Twilio Clientからの電話を、実際の電話につなぎます。
API Serverから返却するべきTwiML
項番3-3で、API Serverから返却されるTwiMLは下記のようになります。
発信元のTwilio電話番号を「050-3123-4567」、発信先の電話番号を「090-5987-6543」としています。
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial timeout="60" callerId="+81-50-3123-4567">
<Number>+81-90-5987-6543</Number>
</Dial>
</Response>
<Number/>エレメントには、発信先の電話番号を指定します。
<Dial/>エレメントのcallerId属性には、必ずTwilio電話番号を指定する必要があります。
https://jp.twilio.com/docs/api/twiml/dial#examples-3
Twilio ServerからのHTTP POSTリクエスト
公式ドキュメントには詳しい情報が見当たりませんでした。以下はパケットキャプチャした情報をもとに説明します。
各パラメータの詳細を見ていきましょう。
なお、Twilio Clientからconnectを実行した際、下記のパラメータを付与しています。
- callerPhoneNumber: 発信元となる、自分自身に紐付けられた電話番号。
- callOutgoingPhoneNumber: 発信先となる、実際の電話番号。
パラメータ | 説明(推測ベース) |
---|---|
AccountSid=AC3exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | TwilioアカウントのSID。 |
ApiVersion=2010-04-01 | TwilioのAPIのバージョンか? |
ApplicationSid=AP75zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz | CapabilityTokenに紐付けられたTwiML AppのSID |
Called= | |
Caller=client%3ADeviceId_0001 | 発信元のTwilio ClientのClient Name。 |
CallSid=CA06nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn | 当該通話に付与されるUniqueなIDか? |
CallStatus=ringing | |
Direction=inbound | |
From=client%3ADeviceId_0001 | 発信元のTwilio ClientのClient Name。Callerと同一の値が入る模様。 |
To= | |
callerPhoneNumber=%2B81-50-3123-4567 | Twilio Clientで指定したカスタムパラメータ |
callOutgoingPhoneNumber=%2B81-90-5987-6543 | Twilio Clientで指定したカスタムパラメータ |
デフォルトで、Caller及びFromにClient Nameが設定されます。
必要に応じて、Twilio Clientの実装の中で必要なパラメータを指定することが可能です。
前述したとおり、Twilio ClientがCapability Tokenを取得する際の通信において、API Serverから任意の値を返すことができますので、ここでconnect時に必要なパラメータを渡しておくこともできます。
最後に
通信フロー・データ構造は以上となります。
次からはいよいよ実装に入っていきます。
AWS API Gateway+Lambda実装Walkthrough(前編)