Twilio Functionsを使って、サーバーレスで連続架電を行う

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

電話APIであるTwilioには、非常に多くの利用方法がありますが、その中でも代表的な使い方に「異常を電話で通報する」というものがあります。その名の通り、例えばサーバーに異常が発生したり、夜間にドアが空いたことを検知したりした場合に、自動的に電話をかけるソリューションです。

## 自動で電話をかけるにはどうするか

Twilioが用意している[RestAPI](https://jp.twilio.com/docs/api/rest)を利用します。
例えば、以下のコードを実行するとTwilio経由で電話がかけられます（予め[Node.jsヘルパーライブラリ](https://jp.twilio.com/docs/libraries/node)をインストールしておく必要があります）。

```Node.js
const accountSid = 'TwilioのAccountSID';
const authToken = 'TwilioのAuthToken';
const client = require('twilio')(accountSid, authToken);

client.calls.create({
  url: 'http://demo.twilio.com/docs/voice.xml',
  to: '発信先電話電話番号',
  from: 'Twilioで購入した発信元電話番号',
})
.then((call) => process.stdout.write(call.sid));
```

# 本記事で紹介すること

上記コードのように、Twilioを使うとプログラムから簡単に電話をかけることができます。
しかし、異常通報では次のような要件が課せられることがあります。

- 通報先が一人ではない
- 誰かが出るまで電話をし続けたい

このようなニーズには、上記のような単純なしくみでは上手く行きません。
一般的には、上記のようなコードをループさせるプログラムを作り、自前のサーバー上で実行する必要があります。
これは正しいアプローチであり、Twilioとしても推奨する手法です。
一方で、通報のためだけに自前のサーバーを立てるのは難しいとか、自前のサーバーに異常が発生した場合にどうするかという問題もあります。
そこで本記事では、自前でサーバーを建てずに、しかも複数人に対して、誰かが電話にでるまで架電するしくみを紹介します。そのために、Twilioの管理コンソール内に用意されている「Twilio Functions」を利用します。

#### 注意事項

- 本記事で紹介する方法は、あくまでサンプルです。
- この記事に記載した方法で想定されない動作をした場合に、その責任は一切負いませんので、予めご了承ください。

# 準備

本記事を実行するために必要なものは以下の通りです。

- Twilioのアップグレードアカウント（トライアルアカウントでは、発信先には認証済み電話番号しか指定できません）
- Twilioで購入した050番号を１つ
- RestAPIを呼び出すためのしくみ（上記で紹介したコードなどが実行できるパソコンなど）

## ソースコードの取得

今回の記事で使用するコードは、以下のGitHubにて公開されています。
https://github.com/twilioforkwc/serialCall

```$ git clone git@github.com:twilioforkwc/serialCall.git```
で取得するか、
https://github.com/twilioforkwc/serialCall/archive/master.zip
をダウンロードして解凍してください。


# しくみを理解する

ダウンロードしたフォルダ内に、functions.jsがあるので、こちらを見ながら今回のコードを説明します。

通知先の電話番号は、架電先リストに登録していきます。

```
// 架電先リスト
const callList = {
  "携帯1": "+8190xxxxxxxx",
  "携帯2": "+8180xxxxxxxx",
  "携帯3": "+8180xxxxxxxx",
};
```
Twilioでは、電話番号をE.164形式で指定する必要がありますので、先頭に国番号を付けるのを忘れないようしてください（日本の国番号は+81です）。

架電する発信元番号は、発信元番号として設定します。発信元番号はTwilioで購入した番号のみが利用できます。

```
// 発信元番号
const callFrom = '+8150xxxxxxxx';
```

その後に動作を指示するパラメータが続きます。

```
// ループ回数（1以上の整数、1ならループしない）
const maxLoop = 2;
// 留守電チェック（する: true、しない: false)
const AMD = false;
// 呼び出し秒数
const timeout = 10;
// メッセージ
const message = "異常が発生いたしました。至急確認が必要です。";
```

- ループ回数は1以上を指定してください。0を指定した場合は、誰にも架電はしません。
- AMD（Answering Machine Detection：機械応答検知）は、最近Twilioで実装された機能で、留守番電話やFAXなど人間以外が応答した場合に、それをチェックすることができる機能です。この機能は、まだ正式に日本ではサポートされていないのと、1コール毎に1.125円がかかりますので、有効（true）にする場合は注意が必要です。
- 呼び出し秒数は、相手が出るまでのタイムアウト秒数です。この時間内に相手が応答しなかった場合、次の架電先にコールが移動します。
- メッセージは、相手が応答した場合に通知したい内容を記載します。この文章はTwilioのSay動詞のパラメータとして45〜49行目付近で利用されます。日本語以外にしたい場合などはこの部分を変更してください。

このFunctionでは、Twilioが標準で送ってくるパラメータ以外に、idxとloopという2つのパラメータを取ります。
idxパラメータは、発信先リストの何番目に架電するかを指します（0が先頭）。loopパラメータは何回目のループかを判定します（0が最初のループ）。
Functionsの中からRestAPIで発信を行い、相手が応答した場合、もしくは応答しなかった場合に、自分自身を再度呼び出すしくみになっています。

架電するかどうかのチェック条件は以下のとおりです。
- 応答があったが、人間以外が応答した場合（AMDを有効時のみ）
- 話中であった場合
- 発信が何らかの理由により失敗した場合
- 相手が応答しなかった場合
- 相手側からキャンセル応答（通話拒否）がきた場合
- 初回の呼び出し時

# 構築

## Functionsの作成

1. Twilioの管理コンソールにログインします。
2. ランタイムメニューを開き、Overviewを選択します。
![Fuctions連続架電01.png](https://qiita-image-store.s3.amazonaws.com/0/86046/161dc016-c2e4-c4ec-8f50-a318c75b4eaf.png)

3. YOUR DOMAINの部分のドメイン名をコピーしておきます。
4. ランタイムメニューのFunctionsを選択し、新しいFunctionsを作成します。
5. 雛形のダイアログが表示されたら、「Blank」を選択して、「Create」ボタンを押します。
![スクリーンショット 2017-07-21 10.07.59.png](https://qiita-image-store.s3.amazonaws.com/0/86046/0aa9745b-8641-8b47-6468-9c079a4bf0b2.png)

6. FUNCTION NAMEは「連続架電」とします（なんでも良いです）。
7. PATHの部分に「/serialCall」と入力します。
8. ACCESS CONTROLにチェックを外します。
9. CODE欄をすべて削除し、先程ダウンロードしたfunctions.jsの内容を貼り付けます。
![Functions連続架電02 (1).png](https://qiita-image-store.s3.amazonaws.com/0/86046/1a72cee8-3420-d48a-2ce1-0a24371a1213.png)

10. 架電先リスト、発信元番号をそれぞれご自分の環境に併せて変更します。
11. 「SAVE」ボタンを押して保存します。

# テスト

作成したFunctionsをCurlを使って実行してみます。

1. Curlが実行できる環境で、以下のコマンドを入力します。

```lang:Curl
curl -X GET https://xxxxxxx-xxxxxxx-xxxx.twil.io/serialCall
```

ドメイン名の部分は、先程控えておいたご自分のドメイン名に変更してください。
これで連続架電が開始されます。

万が一、コールが止まらずに何度もループしてしまうような場合には、Functionsを一度削除してください。