TwilioとLambdaでIVRからの電話転送設定を自動化する

いよいよ年末です。進捗はいかがでしょうか？

年末年始のお休みに入る前に、電話の転送設定を切り替えたり、留守番電話の案内アナウンスを変更する作業が必要な会社も多いと思います。

最近はインターネットからアクセスできる管理画面が用意されているサービスも増えてきましたが、多くの場合は指定の電話番号に電話をかけ、アナウンスに従って番号を押すといった操作が必要です。

こういったIVR（自動音声応答装置）に対する定型操作を、AWS LambdaとTwilioを組み合わせて自動化してしまいましょう。

この記事は [うるる Advent Calendar 2019](https://adventar.org/calendars/4548) 23日目のエントリーです。

# Twilioとは
Twilioは公衆交換電話網(PSTN)を利用した電話の発着信やSMSの送受信などを、ソフトウェア開発者がWeb APIを通して利用できるようにするサービスです。
PSTNに関する知識がなくても、開発者は慣れ親しんだ言語でコンタクトセンターやIVRなどを構築することができます。

# きょうのレシピ
![構成図g](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37155/98b7f045-ace8-26d4-c24e-bcba972cb00d.png)

# 準備するもの
- AWSアカウント（無料枠があります）
- Twilioアカウント（トライアルが使えます）
- 転送設定をする電話回線

# 電話転送サービスの申し込み
NTTのボイスワープなど、ほとんどの電話会社は独自に転送サービスを提供していますが、オプション契約の手続きが必要なケースが多いです。今回の例ではauひかりの固定電話回線を使いました。

# 転送先の設定
あらかじめ転送先の登録を済ませておいて、あとは転送をオン・オフするだけにしておきましょう。
以下はNTT東日本ひかり電話の設定方法ですが、多くの電話会社は同様の設定マニュアルを用意しています。
<img width="994" alt="スクリーンショット 2019-12-20 17.56.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37155/c0642558-eae1-f2fc-a637-e162485e9d12.png">
*[NTT東日本 ひかり電話 ボイスワープ使用説明書](https://flets.com/hikaridenwa/use/pdf/voice.pdf)より*

# Twilioの着信電話番号の取得
発信に使う電話番号を取得します。Twilioのコンソールから好きな番号を選びましょう。

*※一部の電話会社では、Twilioで取得できる050から始まる電話番号からの転送設定ができない場合があります。*

# TwiMLの作成
TwiMLとは、電話の発着信時の動作を指定するための、XMLをベースにしたマークアップ言語です。メッセージや音楽を流したり、他の番号に転送したり、さまざまなアクションを指定できます。

**TwiML™ for Programmable Voice**
https://jp.twilio.com/docs/voice/twiml

### TwiMLを使ったHello Worldの例
```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Hello World</Say>
</Response>
```

## 転送の停止をするTwiMLを作成
電話回線（auひかり）の転送設定用の番号に電話をかけて、アナウンスに従って数字のボタンを押すという一連の操作をTwiMLで表現します。今回は比較的シンプルな転送停止の操作を作ります。

基本的にはアナウンスを待ちながらDTMF（いわゆるプッシュ信号）を送信するという流れの繰り返しです。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Pause length="6"/> <!-- 6秒待つ -->
  <Play digits="0312345678#"></Play> <!-- 自分の電話番号と#をプッシュ -->
  <Pause length="6"/> <!-- 6秒待つ -->
  <Play digits="1234#"></Play> <!-- 暗証番号と#をプッシュ -->
  <Pause length="3"/> <!-- 3秒待つ -->
  <Play digits="0"></Play> <!-- 0をプッシュ -->
  <Pause length="8"/> <!-- 8秒待つ -->
  <Hangup/> <!-- 電話を切る -->
</Response>
```

## TwiML Binsに登録
TwiMLはWebアプリケーションに動的に生成させることが多いですが、固定の内容であればTwiML Binsに登録するだけで利用できます。作成したTwiMLを登録するとそのTwiMLを返すURLが生成されます。

**TwiML Bins**
https://www.twilio.com/docs/runtime/tutorials/twiml-bins

# 発信処理の作成
LambdaからTwilio Programmable VoiceのVoice APIを呼び出して、発信コールを作成します。必要なパラメーターは発信元と発信先の電話番号、そして先ほどのTwiMLを返すURLです。

発信コールが作成されるとTwilioから電話が発信され、その後はTwiMLの指定に従って制御されます。

```js
const https = require('https');
const querystring = require('querystring');

exports.handler = event => {
    const data = querystring.stringify({
        To: '+8131234xxxx', // 発信先の電話番号
        From: '+81501234xxxx', // 発信元のTwilioの電話番号
        Url: 'https://handler.twilio.com/twiml/EHxxxxxxxxxx', // TwiMLを返すURL
        Record: true, // 録音する場合はtrue
    });
    const options = {
        method: 'POST',
        hostname: 'api.twilio.com',
        path: `/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json`,
        headers: {
            "Content-type": "application/x-www-form-urlencoded",
            'Content-Length': Buffer.byteLength(data)
        },
        auth: `${TWILIO_ACCOUNT_SID}:${TWILIO_AUTH_TOKEN}`
    };
    const req = https.request(options);
    req.write(data);
    req.end();
};
```

## twilio-nodeを使う
上の例ではnpmパッケージを追加しなくていいように、ビルトインのhttpsモジュールを使ってREST APIにリクエストを送りましたが、twilio-nodeパッケージを使えばもっと簡単に実装できます。

**twilio-node**
https://jp.twilio.com/docs/libraries/node

```js
const twilio = require('twilio');
const client = new twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

exports.handler = async event => {
    await client.calls.create({
        to: '+8131234xxxx',
        from: '+81501234xxxx',
        url: 'https://handler.twilio.com/twiml/EHxxxxxxxxxx'
    });
}
```

## Twilio Functionsを使う
さらに、TwilioにはTwilio FunctionsというLambdaと同様のサービスが用意されています。認証が省略できるほか、必要なSDKがはじめから読み込まれているので、実装はもっとシンプルになります。
ただし、Lambdaのようにスケジュール実行させる仕組みがないので、外部からリクエストを投げて実行する必要があります。

**Twilio Functions**
https://jp.twilio.com/docs/runtime/functions

# トリガーの設定
最後に、この処理をスケジュール実行するためのトリガーを設定しましょう。作成したLambda関数にCloudWatch Eventsのトリガーを追加して、スケジュール式を指定します。日付指定がUTCであることに注意してください。
<img width="832" alt="CloudWatch Events トリガー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37155/e7d19b8a-8cd4-3acf-3465-235934292f6f.png">

# 実行
これで自動設定の仕組みが完成しました。試しに実行してみると、Twilioのコールログから発信履歴が確認できます。録音の設定をしておけば、通話の音声も確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37155/205a559b-f2e4-6769-debf-b04f4012d54d.png)

#### 録音された音声

```
「お客様のご契約電話番号を入力し、最後に＃を押してください」
「お客様の暗証番号を入力し、最後に＃を押してください」
「転送の停止はゼロ…（プツッ）」
「転送を停止しました」
```

あとは転送開始の処理も同様に作成すれば、決まった時間に電話転送の設定が切り替わるようになります。これでまたひとつ、面倒な手作業を減らすことができましたね！

# ついでに電話機もなくしてしまいませんか？
私たちはfondeskという電話代行サービスを開発、提供しています。
かかってくる電話をぜんぶfondeskに転送するだけで、オペレーターが代わりに電話に対応して、用件をSlackなどのチャットツールに報告してくれます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37155/064574e5-373e-3129-88ce-3bcbfd549f4f.png" width="128">  **fondesk** https://www.fondesk.jp/
デベロッパーにとっての集中力と、その維持の大切さは言うまでもありません。しかし、特にスタートアップなどでは、電話対応と完全に無縁な職場環境を作ることが難しいケースもあるのではないでしょうか。fondeskはきっとそんなあなたのお役に立つはずです。初月が半額になるクーポンもあるよ！→ `YS738-74482`

fondesk、そして[株式会社うるる](https://qiita.com/organizations/uluru)では一緒にサービスを作る仲間を探しています。
ご興味のある方はぜひお知らせください。

[うるる Advent Calendar 2019](https://adventar.org/calendars/4548) も残すところあと2日。24日目の明日はknst_ironさんの担当です。