Twilioでエラーが発生した時にSlackに通知したい

# はじめに
Twilioを使った電話処理でエラーが発生した際に、Slackに通知をおこないたい。
この実現方法は、2015年10月に @kimihom さんがAWSを使った記事を公開されているが、今回はAWSなどの外部サービスを使わずに、Twilio Functionsを使って実現する。

-  参考：「[Twilio x AWS Lambda と API Gateway の連携](http://www.bokukoko.info/entry/2015/10/07/Twilio_%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E5%8F%AF%E7%94%A8%E6%80%A7%E3%82%92%E4%B8%8A%E3%81%92%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB) 」

# 全体フロー
一連のフローはこのようになる。

![twilio-fallback-flow.png](https://qiita-image-store.s3.amazonaws.com/0/133603/58850a22-69ed-3906-570a-44770bcb0fae.png)



# 事前準備
- SLACKのIncoming WebhooksのURLを作成しておく(https://api.slack.com/apps/)

# ステップ概要

1. Functionsを設定する
2. 電話番号のエラー処理を設定する
3. 確認する

# 1. Functoinsを設定する

## Configure
まず環境変数と利用するパッケージの設定をおこなう。
Twilioコンソールから、[*Runtime > Functions > Configure*](https://jp.twilio.com/console/runtime/functions/configure)　に移動して以下の設定をおこなう

### Environmental Variables

|KEY|VALUE|
|----|----|
|SLACK_WEBHOOK_URL|https://hooks.slack.com/services/xxxx/yyyy/zzzz|
|SAY_MSG|(任意)|

- SLACK_WEBHOOK_URLには、事前準備で用意したURLを設定する
- SAY_MSGは任意。（デフォルトは「ただいま回線が混みあっております。時間をおいておかけ直しください。」）

### Dependencies

|NAME|VERSION|
|----|----|
|@slack/client|3.16.0|

- 後述のコードで、Slack通知にSlackモジュールを使用しているので設定する
  - 2018/03/14現在、v4.0.0 はバグがあるので1つ前のバージョンを指定している

## Manage
次に、Functionを作成する

Twilioコンソールから、[*Runtime > Functions > Manage*](https://jp.twilio.com/console/runtime/functions/manage)　に移動して、Functionを作成する
「＋」ボタンを押して、Blankを選択して[Create]。以下の設定をおこなう。

### Properties

|Name|Value|
|----|----|
|FUNCTION NAME|Fallback Handler|
|PATH|/fallback|

- FUNCTION NAME, PATHは任意

### Configuration

|Name|Value|
|----|----|
|ACCESS CONTROL| checked |
|EVENT|Incoming Voice Calls|
|CODE|下記参照|

- ACCESS CONTROLは任意
 
### Code
```javascript
const { IncomingWebhook } = require('@slack/client')

exports.handler = function(context, event, callback) {
    console.log(context, event)
    const msg_say = context.SAY_MSG || "ただいま回線が混みあっております。時間をおいておかけ直しください。"
    const slack_url = context.SLACK_WEBHOOK_URL
    const msg = `
        電話エラー発生！！
        AccountSid: ${event.AccountSid}
        From: ${event.From}
        To: ${event.To}
        Direction: ${event.Direction}
        Logs: <https://jp.twilio.com/console/voice/calls/logs/${event.CallSid}|Link>`.replace(/^\s+/mg, '')
    const slack = new IncomingWebhook(slack_url)
    const msg_slack = {
        text: msg,
        icon_emoji: ":ghost:"
    }
    slack.send(msg_slack, function(err, res) {
        if (err) {
            console.log('Slack Error:', err)
        } else {
            console.log('Slack Message sent: ', res)
        }
        let twiml = new Twilio.twiml.VoiceResponse()
        twiml.say({voice: 'woman', language: 'ja-JP'}, msg_say)
        callback(null, twiml)
    });
}
```

# 2. 電話番号のエラー処理を設定する

Twilioコンソールから、[*Phone Numbers*](https://jp.twilio.com/console/phone-numbers/incoming) に移動して、電話番号を選択する
*設定 > 音声およびFax* の「メインハンドラー失敗時」に1で作成したFunction(Fallback Handler) を指定する

# 3. 確認する

*設定 > 音声およびFax* の「通話着信時」のWebhook URLを存在しないものに変更して、Twilioが404エラーを受信するようにする
電話番号に電話をかけて、設定した音声メッセージが流れること、Slackにメッセージが届くことを確認する

![twilio-fallback-slack.png](https://qiita-image-store.s3.amazonaws.com/0/133603/2c0e8aa0-f199-5c19-1e3d-ed78cbc9132b.png)

確認したら、Webhook URLを元に戻すのを忘れずに！

# まとめ・最後に
- Twilio の電話処理でエラーが発生した時にSlackに通知させることができた
- AWSなどの外部サービスを使用せずに、Twilioだけで完結できた
- *Runtime > Alert Triggers* を使えば電話番号ごとではなく包括的に同様の設定ができそう？

# 追記
- タイトルの「Twilioでエラーが発生した時」とは、正確には「Twilioがリクエストを投げた先のアプリ・サーバがエラーを戻した時」です。  
アプリ・サーバが大きくない場合は、Twilioとアプリを一つのものとして認識していることも多いため、タイトルの表記としてます。