twilio
TwilioDay 23

SIP Registrationで確実に発信者番号を通知させる

はじめに

本記事は、Twilio Advent Calendar 2017の23日目の記事となります。
また、SIP Registrationについては、同じく18日目に記事を書いていただいております。ありがとうございます。
SIPレジストを使って、Twilio環境だけでSIPソフトフォンを使ってみた
本記事は、上記記事を補足する形になりますので、実装にあたっては、まずは上記記事を読んでください。

SIP Registrationと、その課題について

SIP Registrationは、SIPサーバーを自前で用意せずに、SIP端末(固定電話やSIPフォン)をTwilioから扱うことができる、とても便利で実用的な機能です。SIPを利用することで、WebRTCより安定した音声通話を実現することができます。
しくみとしては、本来ユーザ側に設置するべきSIPサーバーをTwilio側で持つことによって実現しているのですが、現時点では、Twilioのバージニアリージョン(us1)にしかSIPサーバーがありません。
そのため、日本国内で利用する際には以下のような問題点があります。

  • 発信、もしくは着信時に少し時間がかかる
  • 発信時に、発信者番号が表示されないことがある

前者に関しては、発着信を同じ場所でやらない限りあまり気にならないのですが、後者については導入の障壁になることがあります。
そこで、本記事では、後者を解決する方法を記載しようと思います。
なお、Twilioが日本リージョン上でSIP Registrationをローンチしてくれれば、今回のような回避策は不要になります。

そもそもなぜ発信者番号が通知できないのか

結論から言うと、正しいルートを通らずに通話が行われるからです。
現在Twilioでは、世界100カ国以上の通信事業者とつながっており、それらの国に対して電話をかけたり、受けたりすることができます。そのため、裏側ではどの経路を通って通話を実現するかを細かく調整していますが、極稀に正しくないルートで通話を行ってしまうことがあります。たとえば、本来日本の電話番号宛であれば、日本の通信事業者経由で発信がされるのですが、これが海外の通信事業者経由で発信されてしまうなどです。これによって、発信者番号が正しく表示されず、非通知だったり、+81〜のようなおかしな表示になることがあります。

今回のSIP Registrationでは、この最適ルートの決定がうまくいかないことがわかっており、結果として発信者番号がうまく表示されない事象がでています。

どうやって解決するのか

SIP Registrationからの発信では、上記事象を回避することが難しいので、SIP Registrationから直接発信するのではなく、一旦カンファレンスを経由して、カンファレンスから発信させることで、ルートが正しく計算されるようになります。
今回は、先日GA(Generally Available)になったAgent Conferenceを使い、Agent Conferenceに含まれる、Outbound Conference Call APIを使っていきたいと思います。
ただし、一点注意があります。それは、Agent Conferenceを利用することで、本来SIP Registrationから直接発信するのに比べて、追加費用(0.45円/分)がかかることです。

ハンズオン

手順は以下の2つです。
1. 発信時に、発信者側に流すメッセージをTwiMLで作成
2. 発信用のFunctionを作成

発信時のメッセージをTwiMLで作成する

Twilioの管理コンソールにログインし、RuntimeのTwiML Binsを選択します。
新しいBinsを作成します。
FRIENDRY NAMEは、「WaitUrl」とします(なんでも良いです)。
TWIMLには、以下のコードを記載します。

TwiML
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice='alice' language='ja-JP' loop='0'>只今呼び出しています。このまましばらくお待ち下さい。</Say>
</Response>

SAVEボタンを押してTwiMLを保存したら、このTwiMLのURLをメモ帳にコピーしておきます。

発信用のFunctionを作成

冒頭にご紹介した元記事の、発信用Function(/sipoutという名前で作成されたもの)の代わりに、以下のコードを記載します。

sipout
exports.handler = function(context, event, callback) {
  // 発信元番号(Twilioで購入した番号)
  const from = '+8150XXXXXXXXX'

  // 発信先番号(SIPアドレス形式)を取得
  let to = event.To || ''

  // SIPアドレス(sip:0AB〜J@xxxxxxx)を分解して0AB〜Jの部分を取得する
  to = to.indexOf("@") > 0 ? to.substring(4, to.indexOf("@")) : ''

  // 0AB〜JをE.164に変換
  to = '+81' + to.substring(1)

  // カンファレンスに参加するTwiMLを作成
  let twiml = new Twilio.twiml.VoiceResponse();

  let dialParams = {}
  dialParams.callerId = from
  dial = twiml.dial(dialParams)
  dial.conference({
    region: 'jp1',
    endConferenceOnExit: true,
    waitUrl: '先ほど作成したTwiML BinsのURL',
  }, to)

  // AgentカンファレンスのOutgoing call APIを実行する
  const client = context.getTwilioClient()
  client
    .conferences(to)
    .participants.create({
      region: 'jp1',
      to: to,
      from: from,
      endConferenceOnExit: true,
      earlyMedia: true,
    })
    .then(participant => {
      console.log(participant.sid)
      callback(null, twiml)
    })
    .catch(error => {
      console.log(error)
      callback(error)
    })
};

このプログラムは2つの機能を実現しています。
まず、SIP Registrationから発信されたコールを、カンファレンスに転送しています。その直後に転送したカンファレンスから相手先に電話をしています。
利用するカンファレンスのリージョンは日本(jp1)にし、カンファレンスから発信する際の間を埋めるために、先程作成したTwiMLを利用しています。
カンファレンス名に宛先番号を使っているのは、同時に別の発信をしたときに混信しないようにするためです。

まとめ

冒頭にも記載しましたが、今回の回避策はあくまでTwilioが日本リージョンにSIP Registrationを実装するまでの一時的な手順となります。日本リージョンで利用できるようになりましたら、またFacebookの公式ページ等でお知らせ致します。
Facebookのいいね!も併せてよろしくお願いいたします。