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ソフトフォンを使ってみた][2] をもとに構築したのですが、困った部分が多かったため、私なりにまとめた記事となります。

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

# 事前準備

- twilioのアカウントを作成 https://cloudapi.kddi-web.com/signup
- 日本の電話番号を取得するため、Bundlesの設定 https://qiita.com/mobilebiz/items/579d1920a8b0bca96459
- アカウント登録から数日後にメールで連絡が来る、取引時確認情報の登録 https://cloudapi.kddi-web.com/magazine/other/personal-transaction-confirmation-information-registration-method

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

# 電話番号の取得

左側メニューの三点リーダのマークをクリックし、Phone Numbersをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/555a5733-3e89-c349-d5f0-98f654281db6.png)

左側のメニューの番号を購入をクリックします。今回は050番号を取得したいため、番号の欄に50と入力し、検索ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/5a5d7630-d963-f2be-9853-6f00da27aaea.png)

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

「Review Phone Number」の画面では、番号の利用できる用途が表示されるので、確認して**Next**をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/4b8d32f1-f21f-95f1-cb94-c36dfc823551.png)

「Select an End-User for +8150xxxxxxxx」 の画面では、電話番号をだれが利用するかを選択します。今回は個人向けで電話番号を利用するため、Individualを選択して**Next**をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/a69f3cbb-ba2c-c846-9985-5b856aec602c.png)

「Comply with Regulatory Requirements」の画面では、事前準備の項目で設定したBundlesを指定します。Bundlesを指定すると、住所を設定する画面になるため、こちらも指定します。Bundlesと住所を指定すると、右下の**Buy +8150xxxxxxxx**ボタンがアクティブになるので、クリックすると購入できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/6470819d-a47e-24c3-59ba-d1dc3f096f7a.png)

# SIPドメインの設定 (SIPレジスト)
[こちら][1]の記事を参考に設定しました。

#### IPアクセス制御リストの設定
管理コンソールより、「Programmable Voice」⇒「SIPドメイン」⇒「IPアクセス制御リスト」を選択します。表示された画面の「Create new IP Access Control List」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/f57e879f-6cad-7713-68a4-d6ff68f2fb22.png)

使用するグローバルIPアドレスを指定し、Create ACLを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/421496a1-3770-e2f7-3ac7-314a51550dfc.png)

#### クレデンシャルリストの設定
IPアクセス制御リストの下にある、「クレデンシャルリスト」を選択します。リストの左上にある＋マークを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/f5274e28-78ba-3dd8-1e78-ba092b225106.png)

SIPフォンに設定するユーザーアカウントを入力します。パスワードは12文字以上、大文字、小文字、数字の入力が必須です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/1c6bd144-c033-ffb2-a44c-a9ab542ed7e5.png)

#### SIPドメインの設定
管理コンソールより、「Programmable Voice」⇒「SIPドメイン」⇒「ドメイン」を選択します。リストの左上にある＋マークを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/f1b530f1-d34b-1867-d21e-37ad3d3b6b6a.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/e5c3ce11-91a2-603a-7c1c-cdd8e2d0b8ce.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/f2c227a2-c527-9ff1-a400-4c3b57c40fac.png)


入力が完了したら、左下のSaveをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/da796b59-6bc5-53ba-f740-43eb74d83ea9.png)


# 着信の設定
[こちら][2]と[こちら][4]の記事を参考に設定しました。

### 着信用Functionの設定
管理コンソールより、「Functions」⇒「Functions(Classic)」を選択します。リストの左上にある＋マークを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/57bb71d6-096b-23a6-562f-cc468972dc6d.png)

着信した際にSIPクライアントに転送する処理と、夜間は自動応答メッセージを流すための関数を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/2e57828f-1f8a-b827-f959-211fc7061a31.png)

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

```Javascript: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/cfa63488-0398-7dab-6a84-1b017624cbf7.png)

Configureタブの真ん中あたり、Voice＆Faxの項目のA CALL COMES INの項目に、先ほど作成した関数名を設定し、保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/6db68d61-e73b-46ba-1b93-278f360dc8cf.png)


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

### Agent Conferenceを有効にする
管理コンソールより、「Programmable Voice」⇒「Conferences」⇒「設定」を選択します。「AGENT CONFERENCE」を選択し、保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/0c0efadc-4d3a-b5de-c187-099ee92fee7c.png)

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

管理コンソールより、「Functions」⇒「Functions(Classic)」⇒「設定」を選択します。
Credentialsの項目にある、Enable ACCOUNT_SID and AUTH_TOKENにチェックを入れると利用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/8b58f48a-e0d7-917c-5afc-a98de60328a8.png)

### TwiML Binsの設定
管理コンソールより、「TwiML Bins」を選択します。リストの左上にある＋マークを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/73bf5250-addf-ea4f-306a-05e75058d3eb.png)

FRIENDLY NAMEとTwiMLを入力し、Createをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/b497ac41-e256-f295-c6b0-2fc03c47a0e7.png)

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

作成後、上部のプロパティにURLが表示されます。後でFunctionを作成するときに使用しますので、コピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/bdf5bbe9-3db2-de97-b3e5-9704c9c3c4f6.png)


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

```Javascript: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/20e8a779-99f3-bcc2-268a-61b34081a95a.png)

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

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

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


# 参考にした記事

- [SIPレジストを使って、Twilioで固定電話をつかってみる][1]
- [SIPレジストを使って、Twilio環境だけでSIPソフトフォンを使ってみた][2]
- [SIP Registrationで確実に発信者番号を通知させる][3]
- [Twilio Functionsを使って営業時間内だけ電話を転送する方法][4]

[1]:https://qiita.com/mobilebiz/items/26f91550556088545c49
[2]:https://qiita.com/moroshi/items/44b71dd2843350813b94
[3]:https://qiita.com/mobilebiz/items/4293e35ebb8de9ca8cc3
[4]:https://qiita.com/friendtree16/items/b8a6d1c1218842ee10a0