Twilio Functionsのためのローカル開発環境

# はじめに

本業とは関係ないところ（父と近所の手伝い）で、Twilioを使った電話システムの開発に初めてチャレンジしてみました。

極力サーバーの構成はシンプルにしたいので、[Twilio Functions](https://jp.twilio.com/docs/runtime/functions)を使いました。AWS Lambdaみたいな感じでサーバーレスにTwilioのシステムを構築できるのが、とても良いですね！

[Twilio Functionsの設定画面](https://jp.twilio.com/console/runtime/functions/manage)で直接Node.jsのコードを書いていけば作れるんですけど、やっぱりローカルのエディタでコード書いて、Gitにスムーズにコミットしたりしたいじゃないですか。

日本語の情報が全くなかったんですが、ちゃんと調べたらとても簡単にできたので、メモがてら残しておきます。

# Functionsとローカル開発の違いで面倒臭いところ

Functionsは全体をこんな関数でくくって、終了時は`callback`を呼び出します。普通にNode.jsサーバー立てる場合はこんなことしない。

```js:Functions
exports.handler = function(context, event, callback) {
  // ...
  callback(null, "Everything is gunna be alright");
}
```

そして、`twiml`や`client`オブジェクトの作り方も微妙に違います。

```js:local
const twiml = new require('twilio').twiml.VoiceResponse();
const client = require('twilio')(ACCOUNT_SID, AUTH_TOKEN);
const callerId = process.env.CALLER_ID;
```

```js:Functions
const twiml = new Twilio.twiml.VoiceResponse();
const client = context.getTwilioClient();
const callerId = context.CALLER_ID; // 環境変数はcontextに全部入る
```

こういうのを、「全部Functionsの仕様に寄せて書いたらローカルで動く」ようにしておきたいわけです。


# twilio-runを使おう

最初は自分で`callback`関数を作ったり頑張ろうとしかけたんですが、GitHubを探したらこんな素晴らしいCLIツールを見つけました！車輪の再発明せずに済んでよかった。。
https://github.com/dkundel/twilio-run

以下、twilio-runを使った構築手順です。前提条件はこんな感じ。

* Twilio電話番号が1つ取得済み
* 開発デバイスはMac
* HomebrewとNode.jsがインストール済み

## Twilio Functionsの設定
一番シンプルな構成として、サンプルで用意されてる「Hello Voice」を使って新規Functionを作成します。
<img width="801" alt="スクリーンショット 2019-01-03 0.59.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/35328334-2dc4-fe05-858d-e109b97f0589.png">

* PATHは`/hello`
* Check for valid Twilio signatureはチェック
* EVENTはIncoming Voice Callsを選択

<img width="876" alt="スクリーンショット 2019-01-03 0.59.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/45852b23-3d66-3407-3ec6-2c56b14bf6c5.png">

こいつをローカルにコピペしてそのまんま動かすのが今回のゴール。

## ローカルの設定

ngrokインストール

```
brew cask install ngrok
```

プロジェクト作成

```
mkdir hello-twilio
cd hello-twilio
npm init -y
```

twilio-runインストール

```
npm install twilio-run --save-dev
```

functionsディレクトリ作成
twilio-runは、実行時に`./functions`配下の全コードを自動的にホスティングするので、必ずこの名前で作るのがポイントです。

```
mkdir functions
```

Functionのソースコード作成
`./functions`配下に作成します。先ほどTwilio上で作った`Hello Voice`をコピペ！[^1]

[^1]: 僕はセミコロン無しでJavaScript書くのが好きなんですが、Twilio Functionsに貼り付けたときセミコロンが無いと「Missing semicolon」と警告が出ちゃって気持ち悪いので、Twilio向けのコードだけはPrettierでセミコロン付ける設定にしてます。

```js:functions/hello.js
exports.handler = function(context, event, callback) {
	let twiml = new Twilio.twiml.VoiceResponse();
	twiml.say("Hello World");
	callback(null, twiml);
};
```
twilio-run起動
デフォルトはポート3000番でホスティングされます。
PATHはJSのファイル名`hello`がそのまんま使われます。

```
$ npx twilio-run
┌────────────────────────────────────┐
│                                    │
│   Twilio functions available at:   │
│   => http://localhost:3000/hello   │
│                                    │
│                                    │
│   ⚠ No assets directory found      │
│                                    │
└────────────────────────────────────┘
```

ngrok起動
ローカルのhttpサーバーを外部公開してくれます。
Forwardingのサブドメインは毎回変わるので、[固定したければ有償プランが必要](https://qiita.com/makopo/items/338791261f86dfec1adf)です。

```
$ ngrok http 3000

ngrok by @inconshreveable                                            (Ctrl+C to quit)

Session Status                online
Session Expires               7 hours, 59 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://example.ngrok.io -> localhost:3000
Forwarding                    https://example.ngrok.io -> localhost:3000
```

ブラウザでhttpsの方にアクセスしてみましょう。
`https://example.ngrok.io/hello`
<img width="540" alt="スクリーンショット 2019-01-03 1.52.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/383b9371-6eac-79ad-af2b-c7211897ee6e.png">

こんなXMLが表示されたら、ローカル側は準備完了！

## Twilio電話番号を開発環境に向ける

電話をかけたらローカルのプログラムが動くようにTwilio側を設定します。「通話着信時」に「Webhook」として先ほどのURLを登録すればOK。

<img width="829" alt="スクリーンショット 2019-01-03 1.54.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/a4ae977f-dcf9-32fd-09ce-f5f64864b660.png">


## 電話をかけてみよう

該当の番号に電話をかけると、英語で「Hello World」と聞こえますね！
こんな風に書き換えて`twilio-run`を再起動すると、日本語でも喋ってくれますね！

```js:functions/hello.js
exports.handler = function(context, event, callback) {
  let twiml = new Twilio.twiml.VoiceResponse();
  twiml.say("世界よこんにちは", { language: "ja-jp" });
  callback(null, twiml);
};
```

## 本番環境に反映

一通りローカルでの開発が完了したら、Functionsの設定画面で最初に作った「Hello World」コードの中身を、ローカルの「hello.js」の内容に置き換えて保存します。（何も変更しなくて良いのが最高！）

その上で、電話番号の設定画面をFunctionに向けます。

<img width="853" alt="スクリーンショット 2019-01-03 2.08.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/24333/5b24d0b8-201e-d07b-8335-cd78dbdddf62.png">

ローカルでスムーズに開発し、サーバーレスなTwilio Functionsで本番運用する体制がこれで整いました！めでたしめでたし。

# おわりに

[kintone hack 2018](http://ascii.jp/elem/000/001/775/1775872/)の戦友、高橋さんの記事が取っ掛かりとして大変参考になりました。Twilioについてググってヒットするのは、半分以上が高橋さんの記事ですねw エバンジェリスト流石っす！
[TwilioのFunctionsを使ったサーバーレス受付電話システム ](https://qiita.com/mobilebiz/items/b9117ec179d5dc18e870)

実際に僕が作ったプログラムは、3つのFunctionが連携する、もう少しだけ複雑なシステムです。近々そちらのコードも公開しますねー。

2019/04/09 公開しました！
解説記事を丁寧に書く時間がなかなか取れそうにないので、ドキュメント一切無しだけど晒してしまいますw
https://github.com/the-red/twilio-neighborhood-network

田舎のガラケーしか持っていないオッチャン達が使うには、「音声電話だけで100%完結するシステム」って超便利だなーと今回思いました。Twilioの面白さに目覚めることができたので、今後も色々と作ってみたいと思います :blush: 
次のステップは、モチロンkintone連携だな！