はじめに
本業とは関係ないところ(父と近所の手伝い)で、Twilioを使った電話システムの開発に初めてチャレンジしてみました。
極力サーバーの構成はシンプルにしたいので、Twilio Functionsを使いました。AWS Lambdaみたいな感じでサーバーレスにTwilioのシステムを構築できるのが、とても良いですね!
Twilio Functionsの設定画面で直接Node.jsのコードを書いていけば作れるんですけど、やっぱりローカルのエディタでコード書いて、Gitにスムーズにコミットしたりしたいじゃないですか。
日本語の情報が全くなかったんですが、ちゃんと調べたらとても簡単にできたので、メモがてら残しておきます。
Functionsとローカル開発の違いで面倒臭いところ
Functionsは全体をこんな関数でくくって、終了時はcallback
を呼び出します。普通にNode.jsサーバー立てる場合はこんなことしない。
exports.handler = function(context, event, callback) {
// ...
callback(null, "Everything is gunna be alright");
}
そして、twiml
やclient
オブジェクトの作り方も微妙に違います。
const twiml = new require('twilio').twiml.VoiceResponse();
const client = require('twilio')(ACCOUNT_SID, AUTH_TOKEN);
const callerId = process.env.CALLER_ID;
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を作成します。
- PATHは
/hello
- Check for valid Twilio signatureはチェック
- EVENTはIncoming Voice Callsを選択
こいつをローカルにコピペしてそのまんま動かすのが今回のゴール。
ローカルの設定
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
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
こんなXMLが表示されたら、ローカル側は準備完了!
Twilio電話番号を開発環境に向ける
電話をかけたらローカルのプログラムが動くようにTwilio側を設定します。「通話着信時」に「Webhook」として先ほどのURLを登録すればOK。
電話をかけてみよう
該当の番号に電話をかけると、英語で「Hello World」と聞こえますね!
こんな風に書き換えてtwilio-run
を再起動すると、日本語でも喋ってくれますね!
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に向けます。
ローカルでスムーズに開発し、サーバーレスな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の面白さに目覚めることができたので、今後も色々と作ってみたいと思います
次のステップは、モチロンkintone連携だな!
-
僕はセミコロン無しでJavaScript書くのが好きなんですが、Twilio Functionsに貼り付けたときセミコロンが無いと「Missing semicolon」と警告が出ちゃって気持ち悪いので、Twilio向けのコードだけはPrettierでセミコロン付ける設定にしてます。 ↩