アラート発生時に 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 の順序制御用。
{
"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 も適当にサクッと作成。
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 で処理しています。
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 通知は無視するよう修正