Edited at

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


はじめに

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

極力サーバーの構成はシンプルにしたいので、Twilio Functionsを使いました。AWS Lambdaみたいな感じでサーバーレスにTwilioのシステムを構築できるのが、とても良いですね!

Twilio Functionsの設定画面で直接Node.jsのコードを書いていけば作れるんですけど、やっぱりローカルのエディタでコード書いて、Gitにスムーズにコミットしたりしたいじゃないですか。

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


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

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


Functions

exports.handler = function(context, event, callback) {

// ...
callback(null, "Everything is gunna be alright");
}

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


local

const twiml = new require('twilio').twiml.VoiceResponse();

const client = require('twilio')(ACCOUNT_SID, AUTH_TOKEN);
const callerId = process.env.CALLER_ID;


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を作成します。

スクリーンショット 2019-01-03 0.59.55.png


  • PATHは/hello

  • Check for valid Twilio signatureはチェック

  • EVENTはIncoming Voice Callsを選択

スクリーンショット 2019-01-03 0.59.31.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


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のサブドメインは毎回変わるので、固定したければ有償プランが必要です。

$ 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

スクリーンショット 2019-01-03 1.52.00.png

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


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

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

スクリーンショット 2019-01-03 1.54.51.png


電話をかけてみよう

該当の番号に電話をかけると、英語で「Hello World」と聞こえますね!

こんな風に書き換えてtwilio-runを再起動すると、日本語でも喋ってくれますね!


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に向けます。

スクリーンショット 2019-01-03 2.08.02.png

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


おわりに

kintone hack 2018の戦友、高橋さんの記事が取っ掛かりとして大変参考になりました。Twilioについてググってヒットするのは、半分以上が高橋さんの記事ですねw エバンジェリスト流石っす!

TwilioのFunctionsを使ったサーバーレス受付電話システム

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

2019/04/09 公開しました!

解説記事を丁寧に書く時間がなかなか取れそうにないので、ドキュメント一切無しだけど晒してしまいますw

https://github.com/the-red/twilio-neighborhood-network

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

次のステップは、モチロンkintone連携だな!





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