Twilio Programmable Voice、Python、JavaScriptを使用したブラウザーからの通話の発着信

April 21, 2021
執筆者
Carlos Mucuho
寄稿者
Twilio の寄稿者によって表明された意見は彼ら自身のものです

このチュートリアルでは、Twilio Programmable Voice APIを使用してWebブラウザーで通話を発信または受信するアプリケーションを作成します。また、通話の発信、応答、拒否を可能にするUIも実装します。

このチュートリアルではと、次のようなアプリケーションを作成します。

プロジェクトのデモ

チュートリアルの要件

チュートリアルを進めるには、以下が必要です。

  • 無料または有料のTwilioアカウント。初めてTwilioを使用する場合は、すぐに無料アカウントを作成 してください。このリンク を使用してアカウントを作成し、後ほど有料アカウントにアップグレードする場合は、$10のクレジットを進呈します。
  • 通話の発着信が可能なTwilioの電話番号。保有していない場合は今すぐ取得 してください。
  • Python 3.6以降がインストールされていること。
  • ngrok がインストールされていること。ngrokは、パブリックエンドポイントからローカルで実行されているWebサービスへのセキュアトンネルを作成するリバースプロキシサービスです。ngrokを使用し、作成したアプリケーションとTwilioを接続するためのセキュアなURLを作成します。
  • 通話の発着信が可能な携帯電話または電話。プロジェクトのテストに使用。

基本的なコンセプトとアプリケーションロジック

 このチュートリアルの目的は、ブラウザーでの通話の発着信を可能にするWebアプリケーションを構築することです。Flaskフレームワークを使用し、必要なWebhookの実装とクライアントアプリケーションの提供を行います。

アプリケーションクライアントは、Twilio Client JS SDK (twilio.js)を使用してTwilioデバイスを作成します。

Twilioデバイス は、ブラウザーで通話を発着信するための主要なエントリーポイントです。 TwilioデバイスとTwilioのサーバー間の接続を確立するには、アプリケーションサーバーでアクセストークンを生成する必要があります。

アクセストークン は、クライアント側のアプリケーションに安全に配布できる短時間有効な認証情報であり、音声会話同期ビデオなどのTwilio ClientのSDKの認証に使用できます。Twilioのサーバーでアクセストークンを生成するには、Twilio APIキーを使用する必要があります。

Twilio APIキーは、Twilio APIへのアクセスを許可する認証情報です。APIキーにより、次のことが可能になります。

  • Twilio APIとの認証
  • アクセストークンの作成、取り消し

アクセストークンは、クライアントにTwiMLアプリケーション (TwiMLアプリ)へのアクセスを許可します。Twilioは、アカウント内のTwiMLアプリケーションを使用し、サーバーとのやり取り方法を決定します。

TwiML (Twilio Markup Language)は、通話、SMS、ファックス受信時の動作をTwilioに指定できる一連の命令です。

プロジェクト構造の作成

このセクションでは、プロジェクトディレクトリを作成し、このディレクトリ内にFlaskアプリケーションの標準ディレクトリを作成します。続いて、仮想環境を作成し、アクティブ化します。最後に、このWebアプリケーションをビルドするために必要なPythonパッケージをインストールします。

ターミナルウィンドウを開き、次のコマンドを入力します。

$ git clone git@github.com:CSFM93/tutorial-twilio-in-browser-calling-start.git twilio-in-browser-calls
$ cd twilio-in-browser-calls

ここでは、このチュートリアル用に作成されたスタータープロジェクト をクローンしてtwilio-in-browser-callsの名前を設定した後、このプロジェクトディレクトリに移動しています。このプロジェクトには、アプリケーションのビルドに使用するボイラープレートコードが含まれています。

内部には、Flaskアプリケーション用の次の標準ディレクトリがあります。

  • static: すべての静的ファイルが保存されます。
  • templates: すべてのテンプレートが保存されます。

staticサブディレクトリには以下が含まれています。

  • css: すべての        CSSファイルが保存されます。このサブディレクトリの中にあるstyle.cssという名前のファイルは、アプリケーションクライアントUIのスタイル設定に使用されます。
  • images: すべての画像が保存されます。この サブディレクトリの中にあるuser.pngという名前のファイルは、アプリケーションクライアントUIに表示されます。
  • js:  すべてのJavaScriptファイルが保存されます。 このディレクトリの中にはmodals.jsという名前のファイルがあります。このファイルには、templatesディレクトリに保存されているモーダルを管理するためのコードが含まれています。

templatesサブディレクトリには3つのファイルが含まれています(call_in_progress_modal.htmldial_modal.htmlincoming_call_modal.html)。

call_in_progress_modal.htmlファイルには、通話中に表示されるモーダルを実装するHTMLコードが含まれています。このモーダルには、通話の経過時間、相手先の電話番号、通話の終了ボタン(赤色の電話アイコン)が表示されます。次のような表示になります。

 

通話中ページ

dial_modal.htmlファイルには、電話番号をダイヤルするときに表示されるモーダルを実装するHTMLコードが含まれています。このモーダルには、数字パッドとボタン(緑色の電話アイコン)が表示されます。次のような表示になります。

番号ダイヤルページ

incoming_call_modal.htmlファイルには、通話着信時のみに表示されるモーダルを実装するテンプレートのHTMLコードが含まれています。このモーダルには、着信中の電話番号と2つのボタン(緑色の電話アイコンと赤色の電話アイコン)が表示されます。着信コールに応答する場合は緑色のボタン、拒否する場合は赤色のボタンを使用します。

コール着信中ページ

作業ディレクトリ内に仮想環境を作成し、アクティブ化します。UnixまたはMac OSシステムを使用している場合は、次のコマンドを実行します。

$ python3 -m venv venv
$ source venv/bin/activate

Windowsでチュートリアルを実行する場合には、コマンドプロンプトウィンドウに以下のコマンドを入力します。

$ python -m venv venv
$ venv\Scripts\activate

仮想環境の作成とアクティブ化が完了すると、アプリケーションの作成に必要なライブラリーをインストールできます。

$ pip install twilio flask python-dotenv

上記のコマンドでは、Pythonパッケージインストーラーのpipを使用し、本プロジェクトで使用する以下のパッケージをインストールしました。

  • Twilio: Twilio APIと通信するためのPythonパッケージ。
  • Flask: Webアプリケーションを構築するためのPythonマイクロフレームワーク。これを使用し、Twilioとやり取りするためのWebhookの作成、通話を発着信するためのクライアントUIの作成を行います。
  • python-dotenv: ファイルからキーと値のペアを読み出し、それを環境変数として追加するライブラリー。このモジュールを使用し、.env設定ファイルに保存されているTwilio認証情報を取得します。

参考までに、このチュートリアルがリリースされた時点での上記パッケージのバージョンとテスト済みの依存関係を以下に掲載します。

certifi==2020.12.5
chardet==4.0.0
click==7.1.2
Flask==1.1.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
PyJWT==1.7.1
python-dotenv==0.17.0
pytz==2021.1
requests==2.25.1
six==1.15.0
twilio==6.55.0
urllib3==1.26.4
Werkzeug==1.0.1

上記のPythonパッケージとは別に、次のフロントエンドライブラリーを使用します。

  • Bootstrap: 最新のWebサイトを作成するために使用される強力なフロントエンドフレームワーク。
  • Twilio Client JS SDK(Twilio.js): Webブラウザーから音声通話を行うことができるライブラリー。
  • jQueryは: HTMLドキュメントのトラバーサルや操作、イベント処理、アニメーション、Ajaxなどをより簡単にする、高速で小さく機能豊富なJavaScriptライブラリー。DOM操作やイベント処理を行うために使用します。
  • Font Awesome: 広く利用されているアイコンのSVG、フォント、CSSツールキット。アプリケーションクライアントでは、これらによりデザインされたいくつかのアイコンを使用します。

TwiMLアプリケーションの作成

このセクションでは、Twilio Console を使用して「TwiMLアプリ」を作成します。このアプリには、後でビルドするWebhookのURLを保存します。

新しいブラウザーウィンドウを開き、Twilioアカウントのコンソール>[Voice](音声)>[TwiML]>[TwiML Apps](TwiMLアプリ)の順にアクセスします。[Create new TwiML App](新しいTwiMLアプリを作成)ボタンをクリックします。すでに他のTwiMLアプリがある場合は赤い[+]アイコンをクリックします。

 

TwiMLアプリ

[Friendly Name](フレンドリー名)フィールドにTwiMLアプリの名前を入力します(例: in-browser calls)。ここでは、他のフィールドは空白のままにしておきます。[Create](作成)ボタンをクリックし、TwiMLアプリケーションを作成します。

TwiMLアプリページ

TwiML Appsダッシュボードにリダイレクトされます。作成したTwiMLアプリをクリックします。このアプリのページで、SID値を選択してクリップボードにコピーします。

TwiMLアプリページ

プロジェクトのルートディレクトリに.envという名前のファイルを作成し、その中に次の内容を入力します。

TWIML_APP_SID="paste your TwiML app SID here"

Twilio APIキーの作成

次のステップでは、Voice API用のTwilio APIキーを作成します。APIキーはアクセストークンを生成するために使用されます。アクセストークンにより、ブラウザーで実行されているフロントエンドがTwilio APIを呼び出すことができます。

ブラウザーでTwilio Console >[Voice](音声)>[Settings](設定)>[API Keys](APIキー)の順にアクセスします。[Create new API Key](新しいAPIキーを作成)ボタンをクリックします。すでに他のAPIキーがある場合は赤い[+]アイコンをクリックします。

Twilio APIキー

[Friendly Name](フレンドリー名)フィールドにAPIキーの名前を入力します(例: in-browser calls)。[Key Type](キーのタイプ)は[Standard](標準)のままにします。[Create API Key](APIキーを作成)ボタンをクリックしてAPIキーを作成します。

新しいAPIキー

新しいAPIキーに関する情報が記載されたページにリダイレクトされます。[SID]と[Secret](秘密)の値をコピーし、.envファイルにTWILIO_API_KEY_SIDTWILIO_API_KEY_SECRETとして貼り付けます。.envファイルは次のように表示されます。

TWIML_APP_SID="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"

[Got it!]フィールドにチェックを入れ、[Done](完了)ボタンをクリックします。

APIキー作成完了

次は、Twilio Consoleのホームページに移動し、TwilioアカウントのSID値を次のように.envファイルにコピーします。

TWIML_APP_SID="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"
TWILIO_ACCOUNT_SID="paste your Twilio Account SID here"

次に、Twilioアカウントのコンソール>[Phone Numbers](電話番号)>[Manage Numbers](番号の管理)>[Active Numbers](アクティブな番号)の順にアクセスし、このチュートリアル用に購入した番号を選択すると、この番号を設定できるページにリダイレクトされます。[Phone Number](電話番号)フィールドを見つけ、このフィールドの下に表示されている電話番号をコピーし、.envファイルにTWILIO_NUMBERとして貼り付けます。数字の間のスペースは削除しますが、先頭のプラス記号は残し、番号がE.164形式であることを確認します。

Twilioの電話番号

電話番号が追加されると、.envファイルは次のようになります。

TWIML_APP_SID="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"
TWILIO_ACCOUNT_SID="paste your Twilio Account SID here"
TWILIO_NUMBER="paste your Twilio phone number here"

Flaskアプリケーションの作成

このセクションでは、Flaskアプリケーションのロジックを作成します。Flaskアプリケーションにより、フロントエンドでの通話の発着信に必要なサポート機能が提供されます。

アプリケーションサーバーの作成

このサブセクションでは、通話の発着信に必要なエンドポイントを作成します。次のエンドポイントを作成する必要があります。

  • / - アプリケーションUI(クライアント)の提供を担当するエンドポイント。
  • /token - アクセストークンの生成とクライアントへの返送を担当するエンドポイント。
  • /handle_calls - 通話の発着信に必要なTwiML命令の生成を担うエンドポイント。

プロジェクトのルートディレクトリにmain.pyという名前のファイルを作成します。任意のテキストエディタを使用してファイルを開き、次のコードを追加します。

from flask import Flask, render_template, jsonify
from flask import request
 
from twilio.jwt.access_token import AccessToken
from twilio.jwt.access_token.grants import VoiceGrant
from twilio.twiml.voice_response import VoiceResponse, Dial
 
from dotenv import load_dotenv
import os
import pprint as p

ここでは、サーバーアプリケーションの構築に必要なパッケージをすべてインポートしました。

  • flaskは、アプリケーションのエンドポイントを定義するために使用されます。
  • このtwilioパッケージはTwilio APIとのやり取りに使用され、クライアントで作成されるTwilioデバイスを介して通話の発着信を行えます。
  • load_dotenvは、.envファイルからTwilioアカウントの認証情報をインポートするために使用されます。
  • pprintは、Twilioが/handle_callsエンドポイントにコールの存在を通知するリクエストを送信する際の受信データのフォーマットと印刷に使用されます。
  • osは、load_dotenvと併用され、.envファイルに保存されている認証情報を取得するために使用されます。

次のコードをmain.pyファイルの最後に追加します。

load_dotenv()

account_sid = os.environ['TWILIO_ACCOUNT_SID']
api_key = os.environ['TWILIO_API_KEY_SID']
api_key_secret = os.environ['TWILIO_API_KEY_SECRET']
twiml_app_sid = os.environ['TWIML_APP_SID']
twilio_number = os.environ['TWILIO_NUMBER']

app = Flask(__name__)


@app.route('/')
def home():
    return render_template(
        'home.html',
        title="In browser calls",
    )

アプリケーションは最初に.envファイルに格納されている環境変数をインポートします。その際にload_dotenv()を呼び出します。これにより、このアプリケーションに必要な5つの設定変数を取得できます。

次に、Flaskアプリケーションインスタンスをappという名前の変数に作成し、それを使用してアプリケーションのエンドポイント/を作成します。このルートは、後で作成するhome.htmlという名前のテンプレートを提供しています。

次のコードを/ルートの下に追加します。

@app.route('/token', methods=['GET'])
def get_token():
    identity = twilio_number
    outgoing_application_sid = twiml_app_sid

    access_token = AccessToken(account_sid, api_key,
                               api_key_secret, identity=identity)

    voice_grant = VoiceGrant(
        outgoing_application_sid=outgoing_application_sid,
        incoming_allow=True,
    )
    access_token.add_grant(voice_grant)

    response = jsonify(
        {'token': access_token.to_jwt().decode(), 'identity': identity})

    return response

これにより、/tokenエンドポイントが追加されます。このエンドポイントは、アクセストークンをリクエストするクライアントから呼び出されます。

このエンドポイントがトリガーされた後、identityという名前の変数を作成し、Twilio番号を割り当てます。IDはユーザー固有のものであり、複数のデバイスで同時にサインインすることができます。複数のユーザーが使用するアプリケーションサーバーでは、送信されるトークンリクエストに基づいて、ユーザーが誰で、何を許可するかを決定する必要があります。ユーザーの本人確認(ユーザーID)には、既存のログインシステムを使用するか、IDプロバイダー(セッションCookie、APIトークン、APIリクエストの保護に使用されるその他のメカニズム)を使用します。ただし、このチュートリアルでは、私たちが唯一のユーザーであるため、複数のIDを使用する必要はありません。このチュートリアルのために購入したTwilio番号は、この目的に適合しています。

次に、account_sidapi_keyapi_key_secretidentityを使用してアクセストークンを作成します。このトークンの「グラント(認可)」をプロビジョニングすると、トークンを送信したクライアントに実行が許可される操作が決定されます。このアプリケーションでは、Voiceグラントオブジェクトを作成し、先に作成したTwiMLアプリのsidを使用して設定します。

エンドポイントを完成させるため、access_tokenidentityをJSON形式でクライアントに返します。

次のコードを/tokenルートの下に追加します。

@app.route('/handle_calls', methods=['POST'])
def call():
    p.pprint(request.form)
    response = VoiceResponse()
    dial = Dial(callerId=twilio_number)

    if 'To' in request.form and request.form['To'] != twilio_number:
        print('outbound call')
        dial.number(request.form['To'])
        return str(response.append(dial))

    return ''


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=3000, debug=True)

このブロックは、/handle_callsという名前のエンドポイントを追加しています。このエンドポイントは、通話の発着信の都度Twilioから呼び出されます。

このエンドポイントがトリガーされた後、pprintを使用してrequest.formの内容を表示し、続いてTwiML応答オブジェクトとTwiML dialオブジェクトを作成します。dialオブジェクトでは、このチュートリアル用に購入したTwilio番号にcallerIdを設定します。これにより、このアプリケーションを使用して電話をかけたとき、受信者の電話端末にanonymousではなくこの番号が表示されるようになります。

その後、条件付きロジックを使用し、request.formオブジェクトにToという名前のプロパティがあるか、このプロパティの値がtwilio_numberと一致するかを確認します。このテストでは、エンドポイントの呼び出しが通話の発信(受信ではない)向けであったことを確認します。これが最初に処理するケースとなります。

発信リクエストであることを確認した後、ダイヤルする番号をrequest.form['To']の値として設定し、dialオブジェクトをresponseオブジェクトに追加し、responseオブジェクトを文字列としてTwilioに返します。Twilioはこれらの命令を実行し、リクエストされた番号をダイヤルします。

スクリプトの下部は、コマンドラインからスクリプトが呼び出されたときにポート3000でFlask開発サーバーを実行する標準条件です。

アプリケーションクライアントの作成

このサブセクションでは、ブラウザーでの通話の発着信を可能にするフロントエンドを作成します。

ホームページの作成

home.htmlという名前のファイルをtemplatesディレクトリ内に作成します。これを開き、次のコードを追加します。

<!DOCTYPE html>
<html>
<head>
    <title>In browser calls</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" />
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <div class="container">
        <!-- log output -->
        <div class="card text-center log-container">
            <h3>Device log</h3>
            <div id="log"></div>
            <div class="btn-container">
                <button type="button" id="btnOpenNumberPad" class="btn btn-default btn-circle btn-lg">
                    <i class="fa fa-phone fa-flip-horizontal " aria-hidden="true" style="color: green;"></i>
                </button>
            </div>
        </div>

        <!-- Modal dial -->
        {% include 'dial_modal.html' %}

        <!-- Modal call in progress -->
        {% include 'call_in_progress_modal.html' %}

        <!-- Modal incoming call -->
        {% include 'incoming_call_modal.html' %}


        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://media.twiliocdn.com/sdk/js/client/v1.8/twilio.min.js"></script>
        <script type="text/javascript" src="/static/js/main.js"></script>
        <script type="text/javascript" src="/static/js/modals.js" defer></script>
</body>

</html>

このテンプレートは、この後でJavaScriptを使用して作成するTwilioデバイスの状態を監視できるコンソールを表示するページを実装しています。このテンプレートに、このプロジェクトのボイラープレートに付属しているcall_in_progress_modal.htmldial_modal.htmlincoming_call_modal.htmlテンプレートを含めました。このページには、電話アイコンの付いたボタンも表示されます。このボタンを押すたびにモーダルが開き、通話を発信したい番号をこのモーダルに入力します。

このテンプレートには、Bootstrap、jQuery、FontAwesome、twilio.jsファイル以外に次のファイルが含まれています。

  • style.css - アプリケーションのスタイル設定に使用されるCSSの一部が含まれているファイル。
  • main.js - Twilioデバイスを作成し、それをTwiMLアプリケーションに接続するためのコードが含まれているjavascriptファイル。
  • modals.js - アプリケーションのモーダルを管理するためのコードが含まれているjavascriptファイル。

style.cssファイルとmodals.jsファイルは、プロジェクトのボイラープレートリポジトリ内にあります。main.jsファイルは次のサブセクションで作成します。

Twilioデバイスの作成

main.jsという名前のファイルをstatic/jsディレクトリ内に作成します。これを開き、次のコードを追加します。

$(function () {
    var device;

    log("Requesting Access Token...");
    // Using a relative link to access the Voice Token function
    $.getJSON("./token")
        .then(function (data) {
            log("Got a token.");
            console.log("Token: " + data.token);

            // Setup Twilio.Device
            device = new Twilio.Device(data.token, {
                // Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
                // providing better audio quality in restrained network conditions. Opus will be default in 2.0.
                codecPreferences: ["opus", "pcmu"],
                // Use fake DTMF tones client-side. Real tones are still sent to the other end of the call,
                // but the client-side DTMF tones are fake. This prevents the local mic capturing the DTMF tone
                // a second time and sending the tone twice. This will be default in 2.0.
                fakeLocalDTMF: true,
                // Use `enableRingingState` to enable the device to emit the `ringing`
                // state. The TwiML backend also needs to have the attribute
                // `answerOnBridge` also set to true in the `Dial` verb. This option
                // changes the behavior of the SDK to consider a call `ringing` starting
                // from the connection to the TwiML backend to when the recipient of
                // the `Dial` verb answers.
                enableRingingState: true,
                debug: true,
            });

            device.on("ready", function (device) {
                log("Twilio.Device Ready!");
            });

            device.on("error", function (error) {
                log("Twilio.Device Error: " + error.message);
            });

            device.on("connect", function (conn) {
                log('Successfully established call ! ');
                $('#modal-call-in-progress').modal('show')
            });

            device.on("disconnect", function (conn) {
                log("Call ended.");
                $('.modal').modal('hide')
            });

        })
        .catch(function (err) {
            console.log(err);
            log("Could not get a token from server!");
        });

    // Bind button to make call
    $('#btnDial').bind('click', function () {
        $('#modal-dial').modal('hide')

        // get the phone number to connect the call to
        var params = {
            To: document.getElementById("phoneNumber").value
        };

        // output destination number
        $("#txtPhoneNumber").text(params.To)
        

        console.log("Calling " + params.To + "...");
        if (device) {
            var outgoingConnection = device.connect(params);
            outgoingConnection.on("ringing", function () {
                log("Ringing...");
            });
        }

    })

    // Bind button to hangup call

    $('.btnHangUp').bind('click', function () {
        $('.modal').modal('hide')
        log("Hanging up...");
        if (device) {
            device.disconnectAll();
        }
    })

    // Activity log
    function log(message) {
        var logDiv = document.getElementById("log");
        logDiv.innerHTML += "<p>&gt;&nbsp;" + message + "</p>";
        logDiv.scrollTop = logDiv.scrollHeight;
    }

});

ここでは、ブラウザーでの通話の発着信を可能にするTwilioデバイスを作成します。

まず、jQueryが提供するgetJSON()関数を使用してアプリケーションサーバーの/tokenエンドポイントにGETリクエストを送信し、アクセストークンを取得します。トークンを取得した後、twilio.jsとトークンを使用してTwilioデバイスを作成し、それをTwiMLアプリケーションに接続します。

Twilioデバイスを作成した後、デバイスにいくつかのイベントリスナーと、UIを使用してデバイスと対話できるようにするコードを追加します。

通話の発信

このセクションでは、作成したアプリケーションを使用して通話を発信します。先に、アプリケーションを実行し、ngrokを設定し、TwiMLアプリを設定しておく必要があります。

プロジェクトディレクトリで2つ目のターミナルウィンドウを開き、Python仮想環境をアクティブ化し、次のコマンドを実行してアプリケーションを起動します。

$ python main.py
``

Open another terminal window and start `ngrok` on it:

上記のコマンドを実行すると、次のような表示になります。

Ngrok出力

https ngrokのURLをクリップボードにコピーします。次に、Twilioアカウントのコンソール>[Voice](音声)>[TwiML]>[TwiML Apps](TwiMLアプリ)ダッシュボードに移動し、このチュートリアル用に作成したTwiMLアプリを選択します。

TwiMLアプリで音声Webhookを設定する

TwiML アプリ設定の[Voice](音声)セクションを見つけ、ngrokから提供されたhttps://URLの後ろに/handle_callsを付加して「Request URL」(リクエストURL)フィールドに貼り付け、[Save](保存)ボタンをクリックします。これにより、アプリケーションをTwiML Appに接続するWebhookが作成されます。

この例では、ngrok URLはhttps://48dcc810632b.ngrok.io/handle_callsです。URLの最初の部分は、ngrokが起動されるたびに変わります。

次に、マイク付きヘッドセットをコンピューターに接続してからブラウザーを開き、「http://localhost:3000/」をアドレスバーに入力します。次のように表示されます。

 

アプリケーションの初期ページ

Twilio.Device Ready!というメッセージが表示された場合、Twilioデバイスは正常に動作しています。緑色の電話アイコンのあるボタンをクリックすると、ダイヤルモーダルが表示されます。

電話ダイヤラー

パッドの数字を使用して発信したい番号を入力するか、キーボードからパッド上部の入力フィールドに入力し、入力できたら緑色の電話マークをクリックして発信します。

このボタンをクリックすると、ブラウザーからマイク使用の許可を求められます。その場合は許可します。相手先の番号が着信に応答すると、通話中のモーダルが表示されます。

通話中ページ

Flaskアプリケーションを実行しているターミナルに移動すると、Twilioが/handle_callsエンドポイントに送信するリクエストデータが次のように表示されます。

{'AccountSid': 'ACe6c069eb8828c025719e6bbb20d63c75',
 'ApiVersion': '2010-04-01',
 'ApplicationSid': 'AP596a0557d23b118259f2cf355fb3693a',
 'CallSid': 'CAc8356bdd69dec58624588f7d578e5668',
 'CallStatus': 'ringing',
 'Called': '',
 'Caller': 'client:+1xxxxxxxxxx',
 'Direction': 'inbound',
 'From': 'client:+1xxxxxxxxxx',
 'To': '+1xxxxxxxxxx'}
outbound call

プロパティのToの値(発信先の番号)がTwilio番号と一致していないため、handle_callsエンドポイントのifステートメント内のコードが実行されました。

着信通話への応答

前のセクションでは、アプリケーションを使用して通話を発信しましたが、着信はまだ確認していません。通話の着信を可能にするには、main.pymain.jshome.htmlにコードを追加し、このチュートリアル用に購入した番号をTwilio Consoleに設定して着信に対応できるようにする必要があります。

main.pyファイルに戻り、/handle_callsエンドポイントのコードを次のように置き換えます。

@app.route('/handle_calls', methods=['POST'])
def call():
    p.pprint(request.form)
    response = VoiceResponse()
    dial = Dial(callerId=twilio_number)

    if 'To' in request.form and request.form['To'] != twilio_number:
        print('outbound call')
        dial.number(request.form['To'])
    else:
        print('incoming call')
        caller = request.form['Caller']
        dial = Dial(callerId=caller)
        dial.client(twilio_number)

    return str(response.append(dial))

ここでは、elseステートメントを/handle_callsエンドポイントに追加しました。この部分のコードは、着信を受けた番号がこのチュートリアル用に購入した番号である場合、つまり着信コールが発生した場合に実行されます。

TwiML dialオブジェクトのcallerIdrequest.formCallerプロパティの値に設定しています。名前が示すように、CallerはTwilio番号を呼び出している番号です。これは、アプリケーションUIで誰が呼び出しているかを確認できるようにするためです。また、dialオブジェクトのclientには、/tokenエンドポイントにアクセストークンを作成した際に使用したidentityの値を設定します。

着信コールのフローを完了するには、dialオブジェクトをTwiML応答オブジェクトに追加し、この応答オブジェクトを文字列として返します。

main.jsファイルに戻り、device.on(‘disconnect')リスナーの下に次のコードを追加します。

device.on("incoming", function (conn) {
    console.log(conn.parameters)
    log("Incoming connection from " + conn.parameters.From);
    $("#callerNumber").text(conn.parameters.From)
    $("#txtPhoneNumber").text(conn.parameters.From)

    $('#modal-incomming-call').modal('show')

    $('.btnReject').bind('click', function () {
        $('.modal').modal('hide')
        log("Rejected call ...");
        conn.reject();
    })

    $('.btnAcceptCall').bind('click', function () {
        $('.modal').modal('hide')
        log("Accepted call ...");
        conn.accept();
    })

});

ここでは、Twilioデバイスが着信コールを監視し、着信を検出するとコール着信中モーダルを表示できるイベントリスナーを追加しました。

Twilio Console >[Phone Numbers](電話番号)>[Manage Numbers](番号の管理)>[Active Numbers](アクティブな番号) ダッシュボードに移動し、このチュートリアル用に購入した番号を選択します。

電話番号設定の[Voice & Fax](音声とFAX)セクションを見つけ、[Configure With](設定)フィールドで[TwiML App](TwiMLアプリ)を選択します。続いて、[TwiML App]フィールドでこのチュートリアル用に作成したTwiMLアプリの名前を選択します。これにより、このチュートリアル用に購入したTwilio番号が、作成したTwiMLアプリにバインドされます。以降は、この番号が着信を受けるたびに、TwiMLアプリのWebhook URLとその他の設定を取得し、それを使用して通話に応答します。 この例では、https://48dcc810632b.ngrok.io/handle_callsにPOSTリクエストを送信し、発信者番号やその他の有用な情報も含めています。

ブラウザーに戻り、http://localhost:3000/に移動し、表示されたページにTwilio.Device Ready!メッセージが表示されるまで待機します。次に、通話の発信ができるデバイスを使用し、このチュートリアル用に購入したTwilio番号に電話をかけます。着信音が鳴り始めると、コール着信中モーダルが表示されます。

 

コール着信中ページ

通話に応答する場合は緑色の電話アイコンの付いたボタンを押し、通話を拒否する場合は赤色のアイコンのボタンを押します。

Flaskアプリケーションを実行しているターミナルに移動すると、Twilioが/handle_callsエンドポイントに送信するリクエストデータが次のように表示されます。

{'AccountSid': 'ACe6c069eb8828c025719e6bbb20d63c75',
 'ApiVersion': '2010-04-01',
 'ApplicationSid': 'AP596a0557d23b118259f2cf355fb3693a',
 'CallSid': 'CAf0eb0489e54978471b96dd258dff4de9',
 'CallStatus': 'ringing',
 'Called': '+17146134152',
 'CalledCity': 'SILVERADO',
 'CalledCountry': 'US',
 'CalledState': 'CA',
 'CalledZip': '92676',
 'Caller': '+17205752613',
 'CallerCity': '',
 'CallerCountry': 'US',
 'CallerState': 'CO',
 'CallerZip': '',
 'Direction': 'inbound',
 'From': '+17205752613',
 'FromCity': '',
 'FromCountry': 'US',
 'FromState': 'CO',
 'FromZip': '',
 'To': '+17146134152',
 'ToCity': 'SILVERADO',
 'ToCountry': 'US',
 'ToState': 'CA',
 'ToZip': '92676'}
incoming call

 

まとめ

このチュートリアルでは、Twilio Voice APIを使用してブラウザーで通話を発信または受信する方法を学習しました。また、Flaskフレームワークを使用してアプリケーションクライアントを構築し、Twilio Client JS SDKで作成したTwilioデバイスとの対話を可能にする方法も学びました。

アプリケーション全体のコードは、こちらのリポジトリ(https://github.com/CSFM93/twilio-in-browser-calls)にあります。

Carlos Mucuho氏は、モザンビークの地質学者から転身した開発者であり、プログラミングを使用してアイデアを現実に変えることを楽しんでいます。https://github.com/CSFM93