AWS Lambda + Twilio Googleカレンダーのリマインダーを音声通知にして電話させる

# AWSとTwilioを利用して、Googleカレンダーのリマインダーを音声通知してみる
普段からいろいろなリマインダー通知をメールやPush通知で受け取ってますが、結構見落としてしまいがちなのでGoogleカレンダーのリマインダーを音声で電話にお知らせしてくれたらなぁと思って書きました。

## AWSサービス以外で事前に必要なもの
1. AWS SESで利用する、受信用のアドレス(独自ドメイン)
1. Twilioアカウント
1. 電話番号購入費と通話料(いわゆるお金)

## 構成
![Constitution.png](https://qiita-image-store.s3.amazonaws.com/0/121487/e30f5954-fbbf-7991-857b-62f3c6bc9526.png)

大体の流れを書きます。

1. GoogleカレンダーからGmailへリマインダーメール送信
1. Gmailから別アドレス(AWS SES)へ転送
1. SESで受信したデータがS3にPUTされる
1. S3 PUTを条件にLambdaが発火
1. Twilioへ通話データ送信
1. Twilioからユーザーへ発信

手順は多いですが、Lambdaのコード書く以外は画面をポチポチしていくだけでした。

## Twilioで電話番号購入(課金)
まずは[Twilio](https://twilio.kddi-web.com/)でサインアップをします。
### SIDとTOKENの確認
![twilio1.png](https://qiita-image-store.s3.amazonaws.com/0/121487/7b62783b-50b0-6509-8719-e38b4cd73816.png)

サインアップしてログインした後、「コンソールダッシュボード」に遷移して「ACCOUNT SID」と「AUTH TOKEN」の内容をひかえておきます。これは後でLambda関数で使います。
### 電話番号の購入
次に発信に必要な電話番号を購入します。電話番号の購入には課金が必要なので予め「課金」ページにてポイントをチャージしておきます。

ポイントをチャージしたら、「コンソールダッシュボード」から「Buy a Number」へ。電話番号の検索画面が表示されるので
1. 「国」から「Japan(USでも良い)」を選択
1. 「利用可能な機能」の「音声通話」にチェックを入れる
1.  検索

検索結果に購入できる電話番号の一覧が出るはずなので、好きな番号を購入します。
![twilio2.png](https://qiita-image-store.s3.amazonaws.com/0/121487/c010dbbf-5e74-3ba2-b25a-63f2762dac1f.png)

電話番号を購入すると「電話番号」「アクティブな電話番号」に購入した電話番号が表示されているはずです。この番号もLambda関数で利用するのでひかえておきます。

___※ Twilioアカウント取得すると、無料トライアルで電話番号を一つもらえるのでそちらでも良いかもです。私はさっさと課金してしまったので、無料でどのくらい通話できるのか確認しておりません。___

## GoogleカレンダーからGmail、そしてSESへ転送
GoogleカレンダーからGmailへの通知設定は簡単なので書きません。

Gmailは、「設定」-> 「フィルタとブロック中のアドレス」 -> 「新しいフィルタの作成」で条件を「from:(calendar-notification@google.com)」などにし、「次のアドレスに転送する」で転送先アドレスを設定すればOKです。

## AWS SESの設定
AWS SESの設定については、以下の記事詳しく参考になります。
* [AWS SESでメールを受信してS3へ保管してみる](http://qiita.com/foxtrack/items/435bd6a77a04c23e03f3)
* [AWS SESでメールを受信してS3へ保管](http://m-shige1979.hatenablog.com/entry/2016/11/11/080000)

## AWS Lambda
次にLambda関数を作成していきます。
### コード
まずはローカルにコードを書いていきます。
`$ mkdir voice_reminder`<br>
`$ cd voice_reminder`<br>
`$ vim lambda_function.py`<br>

```lambda_function.py
# -*- coding: utf-8 -*-
import os
import re
import json
import email                                                                                                                                  
import base64
import urllib

import boto3
from twilio.rest import Client                                                                                                                
account_sid = os.environ['ACCOUNT_SID']
auth_token = os.environ['AUTH_TOKEN']

# Twilio アカウントページのAccount SIDとAuth TokenをSETする
client = Client(account_sid, auth_token)

s3 = boto3.client('s3')

def lambda_handler(event, context):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf8')

    titleText = None
    timeText = None

    try:
        # Gmailから転送されてきたメールデータを取り出す
        response = s3.get_object(Bucket=bucket, Key=key)
        b = email.message_from_string(response['Body'].read())

        titleRegex = re.compile("タイトル: (.+)")
        timeRegex = re.compile("日時: \d{4}/\d{2}/\d{2} \(.+\) (\d{2}\:\d{2}) ～ (\d{2}\:\d{2}) .+")

        if b.is_multipart():
            for payload in b.get_payload():
                line = base64.b64decode(payload.get_payload())

                # メール本文のタイトルと日時を抽出

                title = titleRegex.search(line)
                if title is not None:
                    titleText = title.group(1)

                time = timeRegex.search(line)
                if time is not None:
                    timeText = time.group(1)

    except Exception as e:
        print(e)
        raise e

    if titleText is not None and timeText is not None:
        twiml = "<Response><Pause length='3'/><Say voice='woman' language='ja-jp'>これはリマインダーです。本日 %s より、%s の予定が入っております。</Say></Response>" % (timeText, titleText)

        # リマインドテキスト(できたTwiML)をTwimlets Echoへ渡し、指定電話番号へ発信する
        call = client.api.account.calls\
                .create(to=os.environ['REMIND_TO'],
                        from_=os.environ['REMIND_FROM'],
                        url="http://twimlets.com/echo?Twiml="+urllib.quote(twiml.encode("utf-8")))
        print(call.sid)
```
コード作成したら、twilio-pythonというSDKを、Lambda関数と同一ディレクトリにインストールします。<br>

`$ pip install twilio -t /path/to/voice_reminder`<br>

SDKをインストールしたらZipファイル化します。

`$ zip -r voice_reminder.zip .`<br>

### AWSにLambda関数を作成する
次にLambda関数を作成します。

1. 「Lambda関数の作成」
2. 「ブランク関数」を選択
3. 「トリガーの設定」にてSES設定時に作成したS3 Bucketを指定します。
![lambda-trigger.png](https://qiita-image-store.s3.amazonaws.com/0/121487/f966bf2d-64d6-ce7e-b323-a10da2eab8ce.png)

  1. トリガーをS3に選択
  2. 「バケット」で予め作成したS3を選択
  3. 「イベントタイプ」は「Put」に設定
  4. プレフィックスを設定(SES設定時にObject key prefixを設定していればそれと同じにする)
  5. トリガーの有効をチェック
4. 「関数の設定」で各項目を設定します。
![スクリーンショット 2017-04-19 18.35.14.png](https://qiita-image-store.s3.amazonaws.com/0/121487/6fe61d07-9951-ae27-00ea-75e3b87f1a2b.png)

  1. 関数の名前を入力
  2. 「ランタイム」はPython2.7を選択
  3. 「コードエントリタイプ」で「.ZIPファイルをアップロード」を選択し、先ほど作成したZipファイルを選択
  4. 環境変数に以下の項目をそれぞれ入力する
 ![lambda-env.png](https://qiita-image-store.s3.amazonaws.com/0/121487/579573ce-0420-4140-fd29-893d0571721a.png)

  1. 「ACCOUNT_SID」にTwilioで取得したAccount SIDをセット
  1. 「AUTH_TOKEN」にTwilioで取得したAuth Tokenをセット
  1. 「REMIND_TO」にリマインド通知させたい電話番号をセット(+8190********など)
  1. 「REMIND_FROM」にTwilioで購入した電話番号をセット(+8150********など)
  5. 「ロール」と「既存のロール」を適宜指定。(S3アクセスするので、IAMで予め作っておく)
  6. 「詳細設定」の「タイムアウト」を30秒くらいに設定
5. 関数の作成をする

## Googleカレンダーにスケジュールをセットする
あとはGoogleカレンダーに予定をセットして、Twilioから音声リマインダーが発信されるか確認します。

Googleカレンダーの説明は特にしませんが、「カレンダー設定」から「通知を編集」->「予定の通知」で通知タイミングを設定できるのでお好みの時間をセットしてみてください。

## おわりに
今回初めてTwilio触ってみて、AWS Lambdaみたいなサーバーレスアーキテクチャと組み合わせると面白いことできそうだなと感じました（今更）