Edited at

SNS + Lambda + Twilio で音声電話をかける v2

More than 3 years have passed since last update.

アラート発生時に AWS Lambda を使って音声電話をかける では単に電話をかけるだけでしたが、


  • 複数人に同時に電話をかけたい


    • 1人だと、その人が電話に出なかったら終了なので



  • 夜から朝方までの時間帯で電話を鳴らしたい


    • 日中はアラートメールで十分



という変更をしたくなってきたので、いろいろ改変。

npm パッケージを使うことになったので、JSベタ貼りではなくきちんとzipでアップロードすることになります。

となると、いろいろ面倒になってくるので、gulp 対応、CoffeeScript 化もついでに行います。


コード


package.json

使う npm パッケージは以下の通り。



  • moment-timezone: 有名な日時ライブラリの timezone 対応版。UTC-JST 変換、時刻判定に。


  • q: 有名な Deferred-Promise ライブラリ。


  • twilio: せっかく npm 使うので、直接 REST API を叩くのではなく、サクッとライブラリで。

以下、dev 用。


  • coffee-script: gulpfile を coffee で書くために。

  • del: clean task でのファイル削除用。

  • gulp: gulp 本体。

  • gulp-coffee: coffee compile 用。

  • gulp-zip: zip 圧縮用。

  • run-sequence: gulp task の順序制御用。


package.json

{

"name": "lambda-twilio-alert",
"version": "1.0.0",
"description": "",
"author": "",
"license": "ISC",
"dependencies": {
"moment-timezone": "^0.4.0",
"q": "^1.4.1",
"twilio": "^2.2.1"
},
"devDependencies": {
"coffee-script": "^1.9.3",
"del": "^1.2.0",
"gulp": "^3.9.0",
"gulp-coffee": "^2.3.1",
"gulp-zip": "^3.0.2",
"run-sequence": "^1.1.1"
}
}


gulpfile

gulpfile も適当にサクッと作成。


gulpfile.coffee

gulp = require 'gulp'

coffee = require 'gulp-coffee'
zip = require 'gulp-zip'
del = require 'del'
runSequence = require 'run-sequence'

gulp.task 'clean', (cb) ->
del 'build', cb

gulp.task 'coffee', ->
gulp.src 'src/*.coffee'
.pipe coffee bare: true
.pipe gulp.dest 'build'

gulp.task 'copy', ->
gulp.src 'node_modules/@(moment-timezone|q|twilio)/**'
.pipe gulp.dest 'build/node_modules'

gulp.task 'zip', ['coffee', 'copy'], ->
gulp.src 'build/**'
.pipe zip 'lambda.zip'
.pipe gulp.dest 'dist'

gulp.task 'build', ->
runSequence 'clean', 'zip'



lambda 用コード本体

本体はこんな感じで。

toNumbers には、配列で電話番号を列挙します。

TwiML については S3 Bucket 等の HTTP アクセス可能な場所に設置してもいいのですが、Twilio Labs に Twimlets なるものがあり、これの Echo を使うと QueryString で渡したデータをそのまま XML としてエコーバックしてくれるので、こちらを使っています。

Twimlets を使うことで、任意のメッセージを動的に喋らせることも簡単になりますね。

また、moment-timezone で Asia/Tokyo に変換、10時-19時の間はスキップするようにしています。

twilio-node は Q の Promise を返す仕組みを持っていますので、複数の makeCall の結果を Promise オブジェクトの配列で持ち、Q.allSettled で処理しています。


src/index.coffee

accountSid = 'YOUR_TWILIO_ACCOUNT_SID'

authToken = 'YOUR_TWILIO_AUTH_TOKEN'
fromNumber = '+81XXXXXXXXXX'
toNumbers = [
'+81XXXXXXXXXX'
'+81XXXXXXXXXX'
'+81XXXXXXXXXX'
'+81XXXXXXXXXX'
'+81XXXXXXXXXX'
]
twiML = '''
<Response>
<Say language="ja-JP" voice="alice">サービス名 アラート通知です。</Say>
<Pause length="1" />
<Say language="ja-JP" voice="alice">障害が発生した可能性がありますので、アラートメール及びシステムの状態を確認して下さい。</Say>
</Response>
'''

https = require 'https'
queryString = require 'querystring'
moment = require 'moment-timezone'
Q = require 'q'
twilio = require 'twilio'
client = new twilio.RestClient accountSid, authToken

exports.handler = (event, context) ->
try
# Skip CloudWatch recovery notification
if event.Records[0].Sns.Subject.match /^OK: /
context.done null, 'Skip notification.'
return
catch e
context.done null, 'Invalid event data.'
return

d = moment().tz('Asia/Tokyo')
day = d.day()
hour = d.hour()
if day >= 1 and day <= 5 and hour >= 10 and hour <= 18
# 平日(月〜金)の10時〜19時は通知を抑制する
context.done null, 'Skip notification.'
return

console.log 'Start twilio call'
promises = []
for to in toNumbers
promises.push client.makeCall
from: fromNumber
to: to
url: "http://twimlets.com/echo?Twiml=#{queryString.escape twiML}"

Q.allSettled(promises).then ->
args = [].slice.apply arguments
for arg in args
if arg.state is 'rejected'
context.done null, "Call failed! Reason: #{arg.reason}"
return
context.done null, 'Call succeeded.'
return



ビルド、更新

gulp build すると、dist 以下に lambda.zip が生成されます。

あとは、AWS CLI を使って update を行います。

aws --region ap-northeast-1 lambda update-function-code --function-name YOUR_FUNCTION_NAME --zip-file fileb://dist/lambda.zip


テスト実行

AWS CLI の invoke でテストができます。Management Console から実行しても OK です。

aws --region ap-northeast-1 lambda invoke --function-name YOUR_FUNCTION_NAME lambda.log


追加予定のアイデア


  • SNS のメッセージ内容(重要度)によって電話のかけ先を変える

  • TwiML のメッセージ内容を具体的な障害内容にする

  • 休日カレンダーを考慮した業務時間の判定


    • このためだけに Google API 対応するのはちょっと面倒。。






  • 6/26 修正


    • 土日はアラートメールを見過ごす可能性が高いので、時間に関係なく電話をかけるように修正




  • 7/1 修正


    • AWS Lambda が東京リージョンで利用可能になったため、コマンドの region を修正

    • CloudWatch の recovery 通知は無視するよう修正