2015年5月8日金曜日

twilioを利用した自動で電話を制御するアプリ


twilioを利用して、必要に応じて対象者へ自動で電話をかけるアプリケーションを作成する.
twilioを利用することで、機械的に電話をかけるだけではなく、
相手が受電したかどうか、などの応答により、その後のアクションを決めることも容易にできるようになる.いわゆるIVRである.

自前で音声サービスのプラットフォームを構築するなら
オープンソースのIP-PBXのasteriskを使い、SIP、RTPをしゃべらせるのが一般的だろう。
(参考) asteriskを使った電話の自動応答システム例

今回はそのプラットフォームをtwilioに任せる。
SIPエンドポイントとしてtwilioに接続し、twilioのAPIとアプリケーションスタックを使い
ボイスアプリを作成することだけに注力する。

(参考)
IVR: Interactive Voice Response
PBX: Private Branch eXchange) 
SIP: Session Initiation Protocol
RTP: Real-time Transport Protocol




【動作概要】
(1) アプリが複数の架電先の中の1番目に電話を発信し、内容を伝える.
  
(2) 受電者が電話のキーパットから1をプッシュしたら終了.
   出ない場合か、
   1以外の数字がプッシュされた場合、
   またはプッシュされない場合は(2)へ.

(3) アプリが次の担当者を選択してコール.

上記を繰り返す.



【前提、環境】
● twilioのアカウントを登録し、account sidとauth token取得しているものとする.
 アカウントが有効かどうかを事前に電話をかけて試験をするとよいだろう.
 簡易的にコマンドひとつで電話をかけられる.

$ curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/[account_sid]/Messages.json' \
--data-urlencode 'To=+81**********'  \
--data-urlencode 'From=+81**********'  \
--data-urlencode 'Body=test' \
-u [account_sid]:[auth_token]


● webサービスのAPIを使うため自分で触れるwebサーバを用意しているものとする.


● twilioのAPIライブラリはRubyを使って制御する.
  ※PHP、Python、.NET、Java、Node.jsなどのライブラリもそろっている.

$ gem install twilio-ruby



【ツール概要】
※全コードはGitHubを参照のこと.

● ツール呼び出し(start.rb)
Phoneクラスのcallメソッドを呼び出すだけである.
Webから使えるようにしてもいいだろう.

phone = Phone.new
phone.call


● 架電(phone.rb)
from、toは電話番号である.
説明が必要なのはurlで指定しているselectだろう.
selectで指定したサーバが提示するxmlに従ってtwilioが応答してくれる.
本ツールでは、HTTPのGET値を元に、応答をcgi制御したいため、
direction.rbにて分岐を制御している.

select="http://#{@server}/direction.rb?page=select"
callback="http://#{@server}/direction.rb?page=callback"

call = client.account.calls.create(
  :from => from,
  :to => to,
  :url => select,
  :method => 'GET',
  :timeout => 15,
  :record => false,
  :status_callback => callback,
  :status_callback_method => 'GET',
  'IfMachine' => 'hangup'
  }


● 分岐制御(direction.rb)
selectメソッドがselectの結果である.
numDigitsで指定している1は、電話のキーパットで入力される期待値である.

def select
  gather_url="http://#{@server}/direction.rb?page=gather"

  print "Content-Type: text/xml\n\n";
  puts <<-EOS
  <Response>
    <Gather action="#{gather_url}" numDigits="1" timeout="10" method="GET">
      <Say voice="woman">Please enter key number.</Say>
    </Gather>
    <Say voice="woman">No number was pushed.</Say>
  </Response>
  EOS
end

受電者の反応結果はactionで指定したページへ飛ばされる.
これもGET値を元にcgiで制御し、必要なメソッドを呼ぶ.

def gather
  print "Content-Type: text/xml\n\n";
  if @digit == 1
    puts <<-EOS
    <Response>
      <Say voice="woman">Confirmed. Stop calling.</Say>
    </Response>
    EOS
  else
    puts <<-EOS
    <Response>
      <Say voice="woman">Call next member.</Say>
    </Response>
    EOS
    #@phone.call
  end
end


● 状態管理(state.rb)
誰にコールするか、対応者は応答したか、などの状態を管理する.
ステータスを排他制御もせずテキストファイルで管理しているが、
DBに入れたほうがいいかもしれない.



【その他】 
音声はtwilioディフォルトではなく、
以下のようなサービスと組み合わせてもおもしろいかもしれない。
Nuance
Rizbell
ALTalk
VoiceText
FineSpeech
ReakSpeak
ボイス ソムリエ