昔、流行った電話投票システムをTwilio Serverlessで蘇らせる

#はじめに
西暦2000年頃、まだiPhoneや地デジもなかった時代、テレビ放送などにおいて視聴者の反応を把握するために、質問の答えごとに電話番号を設定し着信数を集計するというシステムが存在していたらしいです。ある特撮ドラマでは、視聴者からのリアルタイム投票によって放送するストーリを変えるということもしていたようです。
今回は、Twilio Serverlessの調査・学習のため、このシステムを現代に蘇らせました。
もちろん「Serverless」というだけあって、Twilioアカウントを持っているだけでOK、Webサーバーやデータベースサーバーなどは必要ありません。
※当時のシステムではカットスルー(着信の中からピックアップして、オペレーターと話をする)という機能もありましたが、今回は未対応です。

#こんな感じのを作ります
<img width="1244" alt="スクリーンショット 2020-05-12 9.33.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110436/2ee59280-e1cf-69f8-340a-ad75f573065c.png">

#利用シーン
例えば、オンライン配信時に、上記画面の写真・投票用電話番号・カウンター部分をキャプチャ配信し、視聴者に電話投票させる。投票した人の電話番号を取得できるので、配信中に電話をかけて出演してもらう。などの使い方あるかと。
※わざわざ電話である必要がなさそうですけども(笑)

#ソースコード
- ソースコード( https://github.com/takeshifurusato/phone-poll )

#使ったTwilioのサービスやツール
- [Twilio Programmable Voice](https://jp.twilio.com/docs/video)
    - 050番号を購入し、音声通話を実現します。
- [Twilio Functions](https://jp.twilio.com/docs/runtime/functions)
    - Twilioが提供するFaaS。Node.jsで書いたコードを実行します。
    - いわゆる、AWS Lamdbaのようなものです。
- [Twilio Assets](https://jp.twilio.com/docs/runtime/assets)
    - Twilioが提供するウェブホスティング可能なストレージサービスです。
    - いわゆるAmazon S3のようなものです。
- [Twilio Sync](https://jp.twilio.com/docs/sync)
    - 情報を同期させるためのサービス。リアルタイム通信プラットフォーム。
    - 詳しくは[Twilio Syncプログラミングサンプル](https://qiita.com/mobilebiz/items/89d8c0aa6a80dac1793f)が参考になります。
- [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart)
    - Twilioをコマンドラインより操作できる機能
- [Twilio CLI Serverless Toolkit](https://www.twilio.com/docs/labs/serverless-toolkit)
    - Twilio CLIのプラグイン。TwilioのAssetsやFunctionsを使ったApplicationをローカル環境で開発でき、また、Twilio環境に簡単にデプロイできる機能です。

#設置方法
###Twilioアカウント作成→Twilio CLIセットアップします
1. Twilioアカウントを取得します。[[最新版]Twilioのサインアップ](https://qiita.com/mobilebiz/items/932eb21ff6ed36f524d8)が参考になります。
2. Twilio CLIをセットアップします。[Twilio CLI（セットアップ編）](https://qiita.com/mobilebiz/items/456ce8b455f6aa84cc1e)が参考になります。
3. Twilio CLI Serverless Toolkitをセットアップします。[Twilio CLI（サーバーレス開発編）](https://qiita.com/mobilebiz/items/fb4439bf162098e345ae)が参考になります。 

###電話番号を購入します。
Twilioのコンソール(管理画面)から、[電話番号](https://jp.twilio.com/console/phone-numbers/incoming)にアクセスし、＋ボタンで投票用の電話番号を2つ購入します。末尾が01、02のように連番が良いですね。
取得した電話番号は「+81」からメモしておきます。

###TwilioSyncを設定します
Twilioのコンソール(管理画面)から、[Twilio Sync](https://jp.twilio.com/console/sync/services)にアクセスし、＋ボタンでSyncを作成します。
以下の画面が表示されるので、「SERVICE SID」をメモしておきます。
<img width="864" alt="スクリーンショット 2020-05-12 21.22.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110436/41cc06a9-23c5-6745-539c-1920407e2812.png">
引き続き、Twilioコンソール(管理画面)から[Sync→ツール→APIキー](https://jp.twilio.com/console/sync/project/api-keys/)にアクセスし、＋ボタンでAPIキーを作成します。キータイプはStandardです。
作成すると以下の画面が表示されるので、必ずSIDとSECRETをメモしてください。※SECRETはこの画面でしか確認できません。
<img width="1096" alt="スクリーンショット 2020-05-12 21.34.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110436/6a18a5c5-259d-41f1-e843-e1df420a66af.png">

###Twilio Serverlessでプロジェクトを作成します。
すでに作成している場合は、そのままで大丈夫です。

```
twilio serverless:init phone-poll
```

以下のディレクトリが作成されます。

```
./
├── assets
│   ├── index.html
│   ├── message.private.js
│   └── style.css
├── functions
│   ├── hello-world.js
│   ├── private-message.js
│   └── sms
│       └── reply.protected.js
├── .env
├── .gitgnore
├── .nvmrc
├── node_modules
├── package.json
├── package-lock.json
└── package.json
```
サンプルコードですので、assetsとfunctionsディレクトリを、phone-pollのソースコード( https://github.com/takeshifurusato/phone-poll )のassets、functionsディレクトリと差し替えます。
※init時にオプションで「--template blank」を指定するとサンプルは作成されません。

続いて、.env.exampleを参考にし.envに追記します。

```env:.env
ACCOUNT_SID=変更しないTwilioServerlessにて自動発行されたものをそのまま
AUTH_TOKEN=変更しないTwilioServerlessにて自動発行されたものをそのまま
MASTER_ACCOUNT_SID=AC******************************** ←TwilioDashboardに記載されているアカウントSID
SYNC_ACCOUNT_SID=SK******************************** ←Sync-ツール-APIキーにて作成したAPIのSID
SYNC_AUTH_TOKEN=******************************** ←Sync-ツール-APIキーにて作成したAPIのSECRET_KEY
SYNC_SERVICE_SID=IS******************************** ←Sync-servicesにて作成したSyncのSID
PHONE_NUMBER_A=+8105012345601 ←選択肢Aの電話番号
PHONE_NUMBER_B=+8105012345602 ←選択肢Bの電話番号
TITLE_A=チキンカレー ←選択肢Aのタイトル
TITLE_B=サバカレー ←選択肢Bのタイトル
```

※TwilioServerlessが自動的に生成したAPIキー(ACCOUNT_SID,AUTH_TOKEN)が、Syncにアクセスするときに使えませんでした。ハマりました。なぜだろう。。。

画面に表示される選択肢用の画像は、assets/imagesにあります。
ファイル名はそのままで差し替えてください。サイズは同じにしておいたほうが良いです。
assets/images/image_a.jpg ←選択肢Aの画像
assets/images/image_b.jpg ←選択肢Bの画像

###Twilio環境にデプロイします
コマンドは以下です。

```
twilio serverless:deploy
```

デプロイが成功すると以下のようにコンソールに表示されます。

```
Deployment Details
Domain: ****************-dev.twil.io
Service:
   phone-poll (**********************************)
Environment:
   dev (**********************************) 
Build SID:
   **********************************
View Live Logs:
   https://www.twilio.com/console/assets/api/**********************************/environment/**********************************
Functions:
   https://****************-dev.twil.io/env
   https://****************-dev.twil.io/poll
   https://****************-dev.twil.io/sync-token
Assets:
   https://****************-dev.twil.io/images/image_a.jpg
   https://****************-dev.twil.io/images/image_b.jpg
   https://****************-dev.twil.io/index.html
   https://****************-dev.twil.io/index.js
   https://****************-dev.twil.io/style.css

```

このURLはメモしておきます。

###電話番号着信時の処理を設定する。
購入した投票用の電話番号とデプロイしたFunctionsを紐付けます。
Twilioのコンソール(管理画面)から、[電話番号](https://jp.twilio.com/console/phone-numbers/incoming)にアクセスし、各電話番号のVoice & FaxのA CALL COMES IN項目に、デプロイされたpollの値を設定します。
<img width="982" alt="スクリーンショット 2020-05-12 22.08.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110436/62906dbe-d658-0aa9-a45d-3252adca45f2.png">


###画面を表示します。
デプロイされたindex.htmlにアクセスします。
(例) https://****************-dev.twil.io/index.html
<img width="1223" alt="スクリーンショット 2020-05-12 22.10.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110436/433f442d-36cb-1370-6e18-c0f64c7ef32d.png">
「スタート」ボタンを押すと表示がリセットされ、受付中となり集計が開始されます。
画面をリロードしても、受付中であれば保存されている集計情報が表示されます。
「終了」ボタンを押すと、Syncに保存されたデータをすべて削除します。
※Syncは明示的にデータを削除しないと永遠にデータが残り続けますので、画面を閉じる前に「終了」ボタンを押すことをオススメします。
画面下部(青色エリア)に、それぞれの選択肢を投票した電話番号が表示されます。オンライン配信中に、自分の電話で架電、ゲスト出演してもらっても良いかもしれませんね。
※そのときに、電話の発信音(プッシュ音)が配信に載らないように注意してくださいね。

#費用の話
一番費用が高いのは、着信費用ですね。
例えば、1,000人が投票したら、約1,200円位かかります。
Serverless部分は、ほぼ無料と言っていいほど、お安いです。

- Twilio Phone Number
    - 1つの電話番号につき、月$4.5
- Twilio Programmable Voice
    - 1回の着信につき、$0.01/分
- Twilio Functions
    - 月10,000回の実行が無料、それ以降は1アクション$0.0001
- Twilio Assets
    - 月10,000回のリクエストが無料、それ以降は1リクエスト$0.0001
- Twilio Sync
    - 月10,000アクションまでは無料、それ以降は1,000,000アクション$10
    - Syncに接続している端末について、月200時間までは無料、それ以降は1時間あたり$0.0025。

#Twilio CLI Serverless Toolkitについて
今までは、GUIからファイルを選択しアップロードしていました。これがコマンド１つでデプロイできるので、非常に便利になりました。環境変数もGUIから設定するのはちょっと面倒でした。
またプロジェクト毎に異なるFQDNが発行されるのも良い。今までは、1つのアカウントに1つのFQDNしか発行されてなかったので、複数のアプリケーションを作ろうとすると、いろいろ不便でした。
総じて、Twilio CLI Serverless Toolkitのおかげで、かなり便利になったと思います。
ただ、initしたプロジェクトを消す方法がわからず、困ってます。。。

追記：2020/05/15追記
作ったプロジェクトは以下で削除可能でした！ご指摘ありがとうございます。
https://qiita.com/mobilebiz/items/fb4439bf162098e345ae#%E5%89%8A%E9%99%A4

    % twilio api:serverless:v1:services:list
    SID                                 Unique Name  Friendly Name
    ZS********************************  phone-poll  phone-poll 
    % twilio api:serverless:v1:services:remove --sid ZS********************************
    The resource was deleted successfully

#まとめ
- [@mobilebizさん](https://qiita.com/mobilebiz)の記事が丁寧でバリエーションも豊富。素晴らしい。ありがとうございます。
- Twilio CLI Serverless ToolkitによるGUIファイルアップロードからの開放。今後のハンズオンなどが非常に捗るかと思います。
- 今回、SyncのMapを使ったけど、Listでも良かったと思いました。
- 電話番号で重複チェックをすれば、1人1回までの投票などできそうですね。
- カットスルー機能は、そのうち作りたいです。
    - 保留にはQueueを使うかConferenceを使うか迷うとこ。