0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Twilio Clientについて調査しました

Last updated at Posted at 2020-05-14

はじめに

お久しぶりです。k.s.ロジャースの西谷です。

今回はVoIPを実現したい話があり、2回目のTwilioについて調査しました。
電話とブラウザ、ブラウザとブラウザの通話を実現したいと思います。

Client自体はチュートリアルに従えば、コードを書くことなくサーバレスでデプロイして通話するところまで実現出来ます。

しかし、実務の場合はサーバ側でゴニョゴニョしたいことがあると思いますので、サーバ側の実装も含めて調査しました。
弊社のサーバサイドはKotlinに移行している経緯があり、今回はTwilioのJava SDKを参考にKotlinで実装を進めました。

間違いやより良い実装があればコメントにて教えて頂けたらと思います。

開発環境

SDKが古いと一部の機能が実装されていない可能性があるので気をつけてください。

  • サーバ
    • Kotlin + Spring Boot
    • com.twilio.sdk v7.50
  • フロント

前準備

Twilioに登録して準備をします。
必要なものは以下の3つです。

  • アカウントSID
  • AuthToken
  • TwiMLのApplicationSID

アカウントSIDとAuthTokenは設定画面から取得できます。

スクリーンショット 2020-05-01 13.52.43.png

ApplicationSIDはTwiMLのアプリを作成すると取得できます。

スクリーンショット 2020-05-01 14.16.22.png

スクリーンショット 2020-05-01 14.17.44.png

実装の流れ

公式のこちらに詳細説明があります。

開発で用意するのは1. You connect to Twilio3. You respond with TwiML instructionsの部分です。

サーバ側の実装

サーバ側で用意するAPIは2つです。

  • ケイパビリティートークンの発行
  • 通話時のTwiML発行

ケイパビリティートークンの発行

ケイパビリティートークンはクライアント毎に発行するアクセストークンのようなものです。

@PostMapping("/twilio/capability/token")
fun createCapabilityToken(): TokenResponse {
    // 電話をかける or かけられるときの名前
    val clientName = UUID.randomUUID().toString()

    // 送受信情報の設定
    val outgoingScope = OutgoingClientScope.Builder(TWILIO_APP_SID).build()
    val incomingScope = IncomingClientScope(clientName)
    val scopes: List<Scope> = Lists.newArrayList(outgoingScope, incomingScope)

    // トークン生成
    val token = ClientCapability.Builder(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
            .scopes(scopes)
            .build()
    
    return TokenResponse(token.toJwt(), clientName)
}

通話時のTwiML発行

こちらは少しややこしく、電話があったときにTwilioからコールバックされるAPIを実装します。
レスポンスはTwiMLのXMLで、通話先やパラメータを設定出来ます。

callerIdは送信元の電話番号を入れますが、数値である必要がありそうです。
本サンプルのようにクライアント名に英数字が含まれる場合は別途送る必要があります。

/**
* 通話時のTwiML生成
*
* @param To 送信先
* @param From 送信元
* @return xml
*/
@RequestMapping(path = ["/twilio/call"], method = [RequestMethod.POST], produces = ["application/xml"])
fun call(@RequestParam To: String, From: String?): String? {
    return if (isAValidPhoneNumber(To)) {
        val number = Number.Builder(To).build()
        val dial = Dial.Builder().number(number).answerOnBridge(true)
                .callerId("送信元の電話番号").build()

        val response = VoiceResponse.Builder().dial(dial).build()
        response.toXml()
    } else {
        // Parameterを使うことでカスタムパラメータを送信できる
        val parameter = Parameter.Builder().name("from").value(From).build()
        val client = Client.Builder(To).parameter(parameter).build()
        val dial = Dial.Builder().client(client).answerOnBridge(true)
                .callerId("送信元の電話番号").build()

        val response = VoiceResponse.Builder().dial(dial).build()
        response.toXml()
    }
}

/**
* 電話番号とクライアントの判定
*
* @param n 電話番号 or クライアント名
* @return 電話番号であるか
*/
fun isAValidPhoneNumber(n: String): Boolean {
    return """^[\d\+\-\(\) ]+$""".toRegex().containsMatchIn(n)
}

後はこのAPIを公開してTwiMLのRequestURLに登録します。
検証用のサーバがない場合はngrokを使うと便利です!

スクリーンショット 2020-05-01 15.29.00.png

フロント側の実装

こちらの実装はHTML一枚だけです。
HTMLはButtonとInputを配置するだけですので割愛します。

$(function () {
  $.ajax({
    type: "POST",
    url: 'ケイパビリティートークン取得のURL'
  })
    .done(function (data) {
      console.log('Got a token: ', data);

      $("#name").text(data.name);
      Twilio.Device.setup(data.token);
    })
    .fail(function () {
      alert('Could not authenticate!');
    });
});

//ready状態になった時に実行
Twilio.Device.ready(function (device) {
  $("#status").text("Ready");
});
//エラー状態になった時に実行
Twilio.Device.error(function (error) {
  $("#status").text("Error: " + error.message);
});
//接続状態になった時に実行
Twilio.Device.connect(function (conn) {
  $("#status").text("Successfully established call");
});
//通話終了した時に実行
Twilio.Device.disconnect(function (conn) {
  $("#status").text("Call ended");
});

//Callボタン
$("#call").click(function () {
  Twilio.Device.connect({
    // サーバ側のAPIのを実行するときのパラメータ
    To: $("#phone_number").val(),
    From: $("#name").text(),
  });
});

//Hangupボタン
$("#hangup").click(function () {
  //通話終了
  Twilio.Device.disconnectAll();
});

// Twilioからのコールバック
Twilio.Device.incoming(function (conn) {
  // サーバ側で設定したカスタムパラメータ
  $("#status").text("Incoming connection from: " + conn.customParameters.get("from"));

  // 通話承諾
  $("#answer").click(function () {
    conn.accept();
  });

  // 通話拒否
  $("#reject").click(function () {
    conn.reject();
  });
});

参考記事

以下記事を参考にさせて頂きました。
https://dev.classmethod.jp/articles/twilio9/#toc-1
https://qiita.com/tmiki/items/fad1a22ba1fe14d32e34

おわりに

今回はブラウザ間の通話としてTwilio Clientとサーバ側の実装について紹介させて頂きました。
実務での導入を考える場合は同時に複数受信した場合などの処理も考える必要があるかなと思います。

間違い等あればご指摘頂けたらと思います。

Wantedlyでもブログ投稿してます

Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?