41
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

今年もやるよ!AWS Lambda縛りAdvent Calendar 2015

Day 12

SES で受信したメールを Lambda で処理してみた

Last updated at Posted at 2015-12-11

この記事は今年もやるよ!AWS Lambda縛り Advent Calendar 2015の12/12日分の記事です。

Amazon SES のメール受信機能からの Lambda 連携をやったことがなかったので、ちょっとした処理をしてみました。

処理の内容

特定の人からメールを受信した時にツイッター等で通知したいことがありますよね?(ないですか、そうですか)
今までは単純に IFTTT で Gmail と Twitter をつなげてやっていましたが、何回目とか何日ぶりかを含めたツイートにしたかったので、Lambda で作ることにしました。

構成

当初 Gmail -> IFTTT -> Maker.ch -(HTTP POST)-> API Gateway -> Lambda -(HTTP POST)-> Maker.ch -> IFTTT -> Twitter と考えていましたが、なぜか Maker channel の REST request が API Gateway を呼び出せず(憶測ですが、SNI非対応?)、ふと SES でメール受信できたなーと思って使ってみる事にしました。

最終的な構成は以下のような感じです。

構成図

※流行りのCLOUDCRAFTで描いてみました(余談ですけど、このサイト AWS じゃなくて Digital Oceanで動いてるんですね)。

セットアップ

大まかな流れは、

  1. IFTTT に Recipe を作成
  2. Lambda ファンクションを仮作成(受信したメールが確認出来る様に)
  3. SES の Rule 作成
  4. (上記の流れで) Rouet53 に SES認証用のレコードと MX レコードを設定
  5. Gmail から メール転送を設定
  6. DynamoDB Table を作成
  7. Lambda ファンクションを本番コードで更新

といった順番になります。

IFTTT に Recipe を作成

  • レシピ作成で、this に Maker 、That を Twitter で作成します
  • Maker の Event Name には、"twitter" と指定しました
  • Twitter の Action には、"Post a tweet" を選択し、Tweet text には、
{{Value1}}からメール着信、{{Value2}}日ぶり {{Value3}}回目

と入れました

作成が終わったら、Maker Channel の設定画面から、テスト実行を行う事が出来ます。

ifttt_16.png

実際に Twitter に下記のようにツイートされました。

ifttt_17.png

Lambda ファンクション仮作成

後ほど設定する Gmail のメール転送設定のために、先に Lambda の Function を作ってしまいます。
SESのリージョンと合わせる必要があるので、us-east-1 に作成しました。名前は ses としました。

exports.handler = function(event, context) {
	console.log(JSON.stringify(event));
	context.succeed(event);
};

入ってきた event object をそのままダンプするだけのスクリプトですので、特に解説はいらないと思います。

SESのルール作成 & Route53設定

SES のコンソールの左下から、Email Receiving > Rules Sets をクリックし、Create a Receipt Rule をクリック。

ses_01.png

次にメールを受信する対象のドメインまたはアドレスを指定します。今回は jaws.ninja ドメイン宛のメール全てを受け付ける設定とします。
ドメインの認証を行う必要がある場合には、下記のように SES から Route53 の Hosted Zone に必要なレコードを追加してくれます。便利ですねー。

ses_02.png

次に、実行する Action として、先ほど作成した Lambda Function を指定します。

ses_03.png

あとはデフォルトのまま進めて、作成完了です。

Gmailの転送設定

転送先アドレスの追加

まず転送が出来るように、転送先アドレスを追加します。

  • Gmail の設定の「メール転送とPOP/IMAP」タブから、「転送先アドレスを追加」を押します。
  • アドレスは適当に、lambda@jaws.ninja などとします
  • 転送確認のためのメールが、SES を通じて lambda に渡されるので、CloudWatch logs から受け取った内容を確認します

gmail_01.png

subject のところに、確認用コードが書いてあるので、Gmail の方に入力します。
これで転送先アドレスの追加は完了です。

フィルタの設定

特定のアドレスから来たメールが、先ほど追加したアドレスに転送されるように設定します。

まずは抽出条件を指定し、

gmail_02.png

次に処理の内容(先ほど追加したアドレスへの転送)を指定し、フィルタを作成します。

gmail_03.png

以上で Gmail 側の設定は完了です。

DynamoDB の設定

今回は単純に、メールアドレス を Partition Key とするテーブルを作成します。
てっとり早くCLIで作成しました。

$ aws dynamodb create-table --table-name email-count ¥
 --attribute-definitions '[{"AttributeName":"email", "AttributeType":"S"}]' ¥
 --key-schema '[{"AttributeName":"email","KeyType":"HASH"}]' ¥
 --provisioned-throughput '{"ReadCapacityUnits":1, "WriteCapacityUnits":1}'
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/email-count",
        "AttributeDefinitions": [
            {
                "AttributeName": "email",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 1,
            "ReadCapacityUnits": 1
        },
        "TableSizeBytes": 0,
        "TableName": "email-count",
        "TableStatus": "CREATING",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "email"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1449823894.715
    }
}

また、受信をカウントしたいメールアドレスの Item を作成しておく必要があります。下記のようなアイテムを作成しておきました。

{
  "count": 2,
  "email": "moto@example.com",
  "name": "moto",
  "unixtime": 1449829080
}

Lambda ファンクションを再作成

Python で Function を同じ名前で再作成します。

処理の内容は、

  • 受け取ったメールの from アドレスをキーとして DynamoDB の Item を Update します
  • その際、カウントは ADD Action でアトミックに追加します
  • 変更前の unixtime を取得し、現在時刻との差分から、何日ぶりかを計算します
  • IFTTT の Maker channel の API エンドポイントに JSON 形式で POST をします

のような流れです。

当然ですが DynamoDB のテーブルへの UpdateItem 権限が必要ですので、適切に Role を設定してください。

# -*- coding: utf-8 -*-
import boto3
import json
import re
import urllib2
import time

# 使い回すオブジェクトはハンドラ外で定義しましょう
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('email-count')
url = 'https://maker.ifttt.com/trigger/twitter/with/key/KEY'

def lambda_handler(event, context):
    # メアドを event オブジェクトから取得
    email = event['Records'][0]['ses']['mail']['source']
    unixtime = int(time.time())

    # update_item で、count をインクリメント、変更前のオブジェクトを取得
    response = table.update_item(
        Key={'email': email},
        AttributeUpdates={'count': {'Value': 1,'Action': 'ADD'}, 'unixtime':{'Value': unixtime} },
        Expected={'email':{'ComparisonOperator':'NOT_NULL'}},
        ReturnValues='ALL_OLD'
    )
    print response

    # 得られたデータから、IFTTT に渡すパラメータを決定
    name = response['Attributes']['name']
    count = int(response['Attributes']['count'])+1
    days = int( (unixtime - response['Attributes']['unixtime']) / (60*60*24))
    payload = json.dumps({'value1':name, 'value2':str(days), 'value3':str(count)})
    print payload

    # HTTPS POSTでリクエストを送る
    req = urllib2.Request(url, payload, {'Content-Type': 'application/json'})
    f = urllib2.urlopen(req)
    response = f.read()
    f.close()
    return response

デモ

実際にメールを送ってみると...

自動でツイートされました!

まとめ

メールベースで何か処理をしたいというケースは結構あると思いますので、これは新しいクラウドデザインパターンになりえるのではないかなーと思います。

41
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?