search
LoginSignup
2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

twilio Advent Calendar 2020 Day 8

posted at

updated at

twilioのSIPレジスト機能を使用してIP電話を構築する(2020年版)

(2021/7/26追記)2021年7月現在、個人では日本の電話番号を取得できなくなっています。ご注意ください。
https://www.twilio.com/guidelines/jp/regulatory

はじめに

おうち電話用に着信時間を絞ることができる電話を構築したいと考えていたところ、twilioというサービスを見つけたので、試しに構築してみました。
Twilio Advent Calendar 2017 23日目のSIPレジストを使って、Twilio環境だけでSIPソフトフォンを使ってみた をもとに構築したのですが、困った部分が多かったため、私なりにまとめた記事となります。

要件

  • SIPクライアントを利用して外線発信、着信ができるようにする (今回はスマホを利用しますが、実際はIP電話機 or VoIPアダプタを購入して設置する想定です)
  • 昼間は着信させ、夜間は自動応答音声を流す

事前準備

twilioアカウントはトライアルアカウントを利用しています。アカウント作成後、本人確認書類を2か所で提出する必要があるのが面倒です。Bundlesの設定が終われば設定作業ができるのですが、数日かかるので気長に待ちます。(私の場合は金曜に承認依頼を出し、月曜に承認されました。)

電話番号の取得

左側メニューの三点リーダのマークをクリックし、Phone Numbersをクリックします。
image.png

左側のメニューの番号を購入をクリックします。今回は050番号を取得したいため、番号の欄に50と入力し、検索ボタンをクリックします。
image.png

検索ボタンをクリックすると、番号の一覧が表示されます。取得したい番号の右側にある購入ボタンをクリックします。

「Review Phone Number」の画面では、番号の利用できる用途が表示されるので、確認してNextをクリックします。
image.png

「Select an End-User for +8150xxxxxxxx」 の画面では、電話番号をだれが利用するかを選択します。今回は個人向けで電話番号を利用するため、Individualを選択してNextをクリックします。
image.png

「Comply with Regulatory Requirements」の画面では、事前準備の項目で設定したBundlesを指定します。Bundlesを指定すると、住所を設定する画面になるため、こちらも指定します。Bundlesと住所を指定すると、右下のBuy +8150xxxxxxxxボタンがアクティブになるので、クリックすると購入できます。
image.png

SIPドメインの設定 (SIPレジスト)

こちらの記事を参考に設定しました。

IPアクセス制御リストの設定

管理コンソールより、「Programmable Voice」⇒「SIPドメイン」⇒「IPアクセス制御リスト」を選択します。表示された画面の「Create new IP Access Control List」を選択します。
image.png

使用するグローバルIPアドレスを指定し、Create ACLを選択します。
image.png

クレデンシャルリストの設定

IPアクセス制御リストの下にある、「クレデンシャルリスト」を選択します。リストの左上にある+マークを選択します。
image.png

SIPフォンに設定するユーザーアカウントを入力します。パスワードは12文字以上、大文字、小文字、数字の入力が必須です。
image.png

SIPドメインの設定

管理コンソールより、「Programmable Voice」⇒「SIPドメイン」⇒「ドメイン」を選択します。リストの左上にある+マークを選択します。
image.png

あとで設定する項目もあるのですが、現時点で設定する項目は以下です。

  • FRIENDLY NAMEを入力
  • SIP URI(SIPクライアントを設定する先に接続先とするサー名)
  • IP ACCESS CONTROL LISTSに先ほど設定したIPアクセス制御リストを設定
  • CREDENTIAL LISTSに先ほど設定したクレデンシャルリストを設定
  • SIP Registrationを有効化し、クレデンシャルリストを設定

image.png

image.png

入力が完了したら、左下のSaveをクリックします。
image.png

着信の設定

こちらこちらの記事を参考に設定しました。

着信用Functionの設定

管理コンソールより、「Functions」⇒「Functions(Classic)」を選択します。リストの左上にある+マークを選択します。
image.png

着信した際にSIPクライアントに転送する処理と、夜間は自動応答メッセージを流すための関数を作成します。
image.png

コードは以下です。コードを入力後、左下のSaveボタンで保存します。
ユーザー名とドメイン名は環境に応じて修正して下さい。

sipin
// [ユーザー名]@[ドメイン名].sip.us1.twilio.comの形式で入力
const sipClient = 'exampleuser@exampledomain.sip.us1.twilio.com';

exports.handler = function(context, event, callback) {

    // SIP宛先
    const sip = 'sip:' + sipClient;

    const twiml = new Twilio.twiml.VoiceResponse();

    // 現在時間を取得
    let date = new Date();
    // JSTにするため9時間進める
    date.setHours(date.getHours() + 9);
    let currentHour = date.getHours();

    // 6:00から17:59までは着信を許可する
    if (currentHour >= 6 && currentHour < 18){
        twiml.dial().sip(sip);
    } else {
        //お断りメッセージ
        twiml.say({
            voice: 'alice',
            language: 'ja-JP' // 言語を設定しないと、日本語は読まれません。
        }, 'お電話いただきありがとうございます。大変申し訳ございませんが、夜間わ電話応対できません。日中に再度お掛け直しをお願いいたします。');
    }

    console.log(twiml.toString());
    callback(null, twiml);
};

着信電話を関数へ転送する

管理コンソールより、「Phone Numbers」⇒「番号の管理」⇒「アクティブな電話番号」を選択します。事前に取得しておいた電話番号をクリックします。
image.png

Configureタブの真ん中あたり、Voice&Faxの項目のA CALL COMES INの項目に、先ほど作成した関数名を設定し、保存します。
image.png

発信の設定

一番ハマったのが発信の設定です。着信の設定の記事を参考に設定していたのですが、発信者番号が非通知になったり、そもそも音声が届かなかったり等、トラブルが多発しました。情報を探すためにネットを彷徨っていたところ、こちらの記事を見つけて何とかなりました。

Agent Conferenceを有効にする

管理コンソールより、「Programmable Voice」⇒「Conferences」⇒「設定」を選択します。「AGENT CONFERENCE」を選択し、保存します。
image.png

Twilio Account Sid と Auth Tokenを関数中で使用できるようにする

Agent Conferenceを利用するために、関数中でgetTwilioClient()コマンドを使用するのですが、このコマンドを利用する際は事前に設定を行う必要があります。

管理コンソールより、「Functions」⇒「Functions(Classic)」⇒「設定」を選択します。
Credentialsの項目にある、Enable ACCOUNT_SID and AUTH_TOKENにチェックを入れると利用できます。
image.png

TwiML Binsの設定

管理コンソールより、「TwiML Bins」を選択します。リストの左上にある+マークを選択します。
image.png

FRIENDLY NAMEとTwiMLを入力し、Createをクリックします。
image.png

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

作成後、上部のプロパティにURLが表示されます。後でFunctionを作成するときに使用しますので、コピーしておきます。
image.png

発信用Functionの設定

作成手順は着信用Functionの設定と同じであるため、コードのみ紹介します。下記のコードから、発信元番号と、TwiMLのURLを書き換えてください。

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

  // 発信先番号(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;
  let dial = twiml.dial(dialParams);
  dial.conference({
    region: 'jp1',
    endConferenceOnExit: true,
    waitUrl: '[先ほど作成したtwiMLBinsの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);
    });
};

発信用FunctionとSIPドメインの関連付け

管理コンソールより、「Programmable Voice」⇒「SIPドメイン」⇒「ドメイン」を選択し、先ほど作成したドメインを選択します。
Call Control ConfigurationのA CALL COMES INの項目に対し、先ほど作成した関数を設定します。
image.png

接続テスト

以上で接続設定が完了したので、テストを実施します。最初はiPhoneからZoiperを利用しましたが、着信はできたものの発信ができなかったため、AGEPhoneに変更してテストしました。
アプリによって異なる可能性はありますが、AGEPhoneではドメイン、ユーザー名、パスワードを入力することで接続できました。

費用

  • 日本の電話番号 110円/月
  • 着信費用 1円/分
  • 発信費用(携帯) 18.5円/分

トライアルで500円分使用でき、今回のテストで250円分くらい使用しました。発信処理でつまづかなければ、もう少し安く済んだと思います。

参考にした記事

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
What you can do with signing up
2
Help us understand the problem. What are the problem?