StatusCallbackで通話の状態を知る

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

[Twilio Advent Calendar 2015](http://qiita.com/advent-calendar/2015/twilio)　13日目の記事です。
12日目は、maru-kouさんによる「[Twilioで無応答転送](http://bci-blog.blogspot.jp/2015/12/twilio-01.html)」でした。
## はじめに
Twilioで開発を行っていると、たまに以下のような質問を受けることがあります。

- 相手を呼び出しているかを知りたい
- 相手が応答したかを知りたい

Twilioで電話発信をするのは簡単で、TwiMLのDial動詞や、REST APIのclient.call.create()を利用することで、あとは何もしなくてもTwilioが相手先への架電を勝手に行ってくれます。通話という視点で考えると、Twilioに任せておけば勝手に繋いでくれるので問題ないのですが、アプリケーションレベルでは、たとえば「相手が応答したときに特定の処理を行いたい」といった要望が出てきます。
そんなときに活躍するのが、statusCallbackと呼ばれるしくみです。
## statusCallbackとは
###### 図1.StatusCallbackの指定場所
<img width="1004" alt="画像1.png" src="https://qiita-image-store.s3.amazonaws.com/0/86046/4e27c16a-8554-2c05-803c-a3b79233ed8f.png">

Twilioで開発をしたことがある方であれば、一度は目にしたことがある画面ですね。
Request URLというのは、ユーザがTwilioに対して、どのような処理をして欲しいかを記述する命令（TwiML）を取得するためのURLです。
Status Callbackはその下に指定する項目があり、ここで指定したURLに対して、Twilioが**「通話状況に変化があった時」**に通知をしてくれるようになっています。
##通話状況とは
電話には幾つかの通話状態があります。例えば、電話で発信するときは概ね次のような状態を遷移します。

1. 発信を受け付ける
2. 通信事業者に発信を依頼する
3. 通信事業者が相手先番号から相手を特定し呼び出す
4. 相手先の電話が鳴る
5. 相手先が応答する

これを図にしたのが以下です。
###### 図2.Twilioにおける発信時の状態遷移
![status-callback-events-dial.png](https://qiita-image-store.s3.amazonaws.com/0/86046/37f01d9f-8486-b3b6-113a-4b59c05282cb.png)
これはTwilioにおけるDial（発信）の状態遷移を表したものです。
Call Statusというところに注目してください。Twilioでは大きく、3つの状態が発生します。

- initiated: 発信を受け付けてキューに保存した状態です。
- ringing: 相手先を呼び出している状態です。
- in-progress: 相手先が応答して通話をしている状態です。

*Twilioの発信に関しては、原則1秒間に1コールしか発信できませんので、それ以上の発信依頼が来た場合は、Twilioは発信依頼を一時的にキューイングして処理します。（これ以上の速度で発信をすることもできますが、別途費用がかかりますので、詳しくはTwilio事業部にご相談くださいとのことです）。*

この状態が遷移するタイミングで発生するのが、今回の主題でもある**「statusCallbackEvent」**です（図の中のCall Events）。
図をみるとわかるように、発信時にはTwilioからは4つのイベントを受け取ることができます（着信時はCompletedイベントのみが発生します）。

- Initiated: 呼が発信キューに入った時に発火します。
- Ringing: 相手先の呼び出し状態に入った時に発火します。
- Answered: 相手先が応答した時に発火します。
- Completed: 通話が終了した時に発火します。

statusCallbackを利用するために覚えておいて欲しい、とても重要なことがあります。
~~それは、「Completed」以外のイベントを取得するのは、有料（0.015円/イベント）であるということです。~~ そのため、「Completed」以外のイベントを受け取るためには、別途受け取りたいイベントを**発信時に**指定しておく必要があります。この指定がない場合は、「Completed」以外のイベントは発火しません。

## statusCallbackEventの指定方法
実際にstatusCallbackEventを指定するにはどうすれば良いかを説明します。
先ほどの図１を見ると、statusCallbackのURLは指定できますが、statusCallbackEventを指定する場所がありません。
なぜなら、図１は、Twilioにおける電話番号に紐付けられた設定で、言い換えればTwilioがその電話番号に**着信したとき**にどのように動作するかを指定するためのものだからです。着信では、Completed以外のイベントは発生しないので、statusCallbackEventの指定欄がないのです。

では、Twilioで発信するときはどうするかを考えてみましょう。
発信には大きく２つの方法があります。一つはTwiMLのDial動詞を使って発信をする方法で、もう一つはRestAPIを使って発信をする方法です。
### TwiMLのDial動詞で発信する場合の指定方法

TwiMLを使って発信するケースには、大きく以下の２つがあります。

1. 050番号に着信した呼を別の電話番号に転送する場合
2. VoIPアプリケーションから発信する場合

いずれの場合も、Twilioからの要求に応じてDial動詞を使ったTwiMLを返却すれば良いので、このTwiMLの中でstatusCallbackEventの指定を行うことになります。
以下が実際にstatusCallbackEventを指定したTwiMLの例です。

```lang:xml
<Dial callerId="発信に利用する050番号">
  <Number statusCallback="ステータスコールバックを受け取るURL"
          statusCallbackMethod="POST"  
          statusCallbackEvent="ringing answered completed">相手先の電話番号
  </Number>
</Dial>
```
statusCallbackEventは、NumberやClientなどの第２動詞のパラメータとして設定します。この際、通知を受け取りたいイベントは**スペースで区切って指定する**ことを忘れないようにしてください。

### RestAPIで発信する場合の指定方法
Twilioを使って発信する、もう一つの方法はRestAPIを使って発信する方法です。
公式ドキュメントは[こちら](https://jp.twilio.com/docs/api/rest/making-calls)。
例えば、Node.jsヘルパーライブラリを使ってコーディングすると、こんな感じです。

```lang:Node.js
client.calls.create({
    url: 'http://YourServer/xxxx',
    to: '+813XXXXXXXX',
    from: '+8150XXXXXXXX',
    timeout: 60,
    statusCallback: 'http://YourServer/statusCallback', <- statusCallbackを受け取るURL
    statusCallbackMethod: "POST", <- その時のメソッド
    statusCallbackEvent: ["ringing", "answered", "completed"] <- 受け取りたいイベント配列
}, function(err, call) {
    if (err) {
    }
});
```
*※RestAPIで発信する場合はTwiMLでEventを指定するときと異なり、配列で指定する必要があるので注意してください。*

## statusCallbackで通知される内容
最後は、statusCallbackでTwilioから通知される内容について説明します。
statusCallbackでは概ね次のような内容が通知されます。

- CallSid: 通話の識別子
- AccountSid: ユーザのTwilioアカウントSID
- From: 発信者
- To: 相手先
- CallStatus: 通話の状態
- ApiVersion: Twilio APIのバージョン
- Direction: 発着信の種別
- ForwardedFrom: 転送された場合の転送元（日本では利用不可）
- CallerName: 発信者IDの検索結果（オプション）

statusCallbackでは、どのイベントでも上記の内容が戻るため、アプリケーション側ではこの中のCallStatusを見て状態を把握する必要があります。
CallStatusには以下の値が入ります。

- queued: 通話は発信待ち（statusCallbackEventにinitiatedを指定した時のみ）
- ringing: 相手先を呼び出し中（statusCallbackEventにringingを指定した時のみ）
- in-progress: 相手先が応答し、通話中（statusCallbackEventにansweredを指定した時のみ）
- completed: 相手先が応答し、通話が正常に終了
- busy: 相手先からのビジー応答（話中）
- failed: 通話不成立（番号違いなど）
- no-answer: 相手先不応答
- canceled: キューイング中、もしくは呼び出し中に発信をキャンセルした

queued、ringing、in-progressを受け取りたいのであれば、statusCallbackEventにイベントの指定が必要です。それ以外のステータスについては、通話終了時（図２のcompleted）で戻ります。

## まとめ

- Twilioで発信をする場合に、通話の状態を行うためには発信時にstatusCallbackEventを指定する必要があります。
- ~~completed以外のイベントは有料です。~~
- TwiMLとRestAPIでは、statusCallbackEventの指定方法が違うので注意が必要です。