SESでメール受信時に、タイトルと各アドレスを取得する

· ☕ 5

このブログはレンタルサーバーを借りていないので、メールサーバーもありません。

SESでメールを受信し、lambdaで本文を抽出して、gmailに転送していますw

ですが、とりあえずで作ったlambdaなので、色々問題が…。

それぞれ解決していきたいと思います!

現状確認

こんな感じで動いています。

メールサーバー

前に記事も書いてるので、コードとか詳しくはこちら

  • 問題① 送信アドレス、受信アドレス、タイトルが分からない

本文を抽出しているだけなので、本文以外の情報が分かりません。

大問題ww

  • 問題② htmlメールが汚い

htmlメールがすごく読みずらいので、なんとかいい方法を考えたいと思います。

解決編はこちら

  • 問題③ 問題というか改善というか、読む必要のないメールは情報だけ欲しい

例えば、毎月の月報だとか、銀行の振込履歴だとか…。

別に読む必要ないから、いい感じにdynamoかなんかに格納して、終わっておいてほしい

  • 問題④ ポイント付きメールを勝手に押してほしいww

ポイントタウンってポイントサイトやってるんですけどね?

ポイント付きのメールが来るんですよw

あれ、勝手にポイントもらっておいてほしいww

究極の改善というか、チートというかwww

問題①を解決

まずは、ここから対応していきます。

送信アドレス、受信アドレス、タイトルが分からない…。

タイトル
本文

lambdaがね、受信はしているんですよ。

なので、それを認識してあげるだけ!

SQSからのメッセージを確認

lambdaのeventにはこんな感じのデータが送られてくる。

{
    "Records": [
        {
            "messageId": "2a13ef0e-bc7f-41e5-b711-25689de7f5eb",
            "receiptHandle": "AQEBaH9NxRsK5YT~略~",
            "body": "{\"notificationType\":\"Received\",\"mail\":~略~}",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1582892255806",
                "SenderId": "AIDAIYLAVTDLUXBIEIX46",
                "ApproximateFirstReceiveTimestamp": "1582892255874"
            },
            "messageAttributes": {},
            "md5OfBody": "0c43f48330cfb83fcb461e64745xxxxx",
            "eventSource": "aws:sqs",
            "eventSourceARN": "arn:aws:sqs:ap-northeast-1:908725900000:receive-mail-rn",
            "awsRegion": "ap-northeast-1"
        }
    ]
}

メールの中身はこのbodyのなか。

展開するとこんな感じ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
{
    "notificationType": "Received",
    "mail": {
        "timestamp": "2020-02-28T12:17:35.003Z",
        "source": "FKTPxJDawxxxxxp@post2.pointtown.com",
        "messageId": "pcbg7k43or9pkbelps3mpb801blqf8xxxxxxxxxx",
        "destination": [
            "pointxxxx@xxxx.jp"
        ],
        "headersTruncated": false,
        "headers": [
            {
                "name": "Return-Path",
                "value": "<FKTPxJDawsxxxxx@post2.pointtown.com>"
            },
            {
                "name": "Received",
                "value": "from relay.gmo-media.jp ~略~"
            },
            {
                "name": "X-SES-Spam-Verdict",
                "value": "FAIL"
            },
            {
                "name": "X-SES-Virus-Verdict",
                "value": "PASS"
            },
            {
                "name": "Received-SPF",
                "value": "pass ~略~;"
            },
            {
                "name": "Authentication-Results",
                "value": "amazonses.com; spf=pass ~略~;"
            },
            {
                "name": "X-SES-RECEIPT",
                "value": "AEFBQUFBQUFBQUFHbWcrMVRDdG9ueWd2VFJlTDNGZEZjVW5LNCt~略~"
            },
            {
                "name": "X-SES-DKIM-SIGNATURE",
                "value": "a=rsa-sha256; q=dns/txt; b=PykRrgWXF/~略~;"
            },
            {
                "name": "Received",
                "value": "(qmail 8117 invoked by uid 60057); 28 Feb 2020 12:17:32 -0000"
            },
            {
                "name": "From",
                "value": "ポイントタウン 【新着サービス】<pointmail@pointmail.com>"
            },
            {
                "name": "To",
                "value": "pointxxxx@xxxx.jp"
            },
            {
                "name": "Message-Id",
                "value": "<POlSINaz.v1ZKfDxxxxx@sys-smtp51>"
            },
            {
                "name": "Subject",
                "value": "【クリックP付き】♪本日の新着サービスのお知らせ♪2/28"
            },
            {
                "name": "Return-Path",
                "value": "<FETPxJDawsxxxxx@post2.pointtown.com>"
            },
            {
                "name": "Mime-Version",
                "value": "1.0 "
            },
            {
                "name": "Date",
                "value": "Fri, 28 Feb 2020 21:17:29 +0900"
            },
            {
                "name": "Content-Type",
                "value": "multipart/alternative; ~略~"
            }
        ],
        "commonHeaders": {
            "returnPath": "FKTPxJDawsxxxxx@post2.pointtown.com",
            "from": [
                "\"ポイントタウン 【新着サービス】\" <pointmail@pointmail.com>"
            ],
            "date": "Fri, 28 Feb 2020 21:17:29 +0900",
            "to": [
                "pointxxxx@xxxx.jp"
            ],
            "messageId": "<POlSINaz.v1ZKfDgx5e5@sys-smtp51>",
            "subject": "【クリックP付き】♪本日の新着サービスのお知らせ♪2/28"
        }
    },
    "receipt": {
        (SNS)
        }
    },
    "content": "Return-Path: <FKTPxJDawsxxxxx@post2.pointtown.com>\r\nReceived~略~"
}

情報が多くてオーバーフローしそうですが…。

必要そうな情報だけ抜いていきます。

まず、メールの情報はmailの中。

本文の情報はcontentの中にあります。

本文の取り方は前回の記事を読んでいただくとして、今回は、ヘッダー情報を取得していきます。

ヘッダー取得

まず各ヘッダー情報を取得しやすいように変形します。

なぜかね、辞書型でなくて、配列で入ってるんですよ。こいつらは…。

なので、まず配列にします。

1
2
3
4
5
6
7
8
9
    email_header = dict(map(lambda x:(x["name"],x["value"]),message["mail"]["headers"]))
    #タイトル
    print(f'タイトル:{email_header["Subject"]}')
    #To
    print(f'To:{email_header["To"]}')
    #From
    print(f'From:{email_header["From"]}')
    #Date
    print(f'Date:{email_header["Date"]}')

欲しいのはこんなところ。

で、SNSのメッセージにこれを加えていく。

コード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import json
import email
import boto3

def lambda_handler(event, context):
    print(json.dumps(event))
    message = json.loads(event["Records"][0]["body"])
    print(message)

    email_body = message["content"]
    email_object = email.message_from_string(email_body)
    email_header = dict(map(lambda x:(x["name"],x["value"]),message["mail"]["headers"]))

    body = ""
    for part in email_object.walk():
        # ContentTypeがmultipartの場合は実際のコンテンツはさらに
        # 中のpartにあるので読み飛ばす
        if part.get_content_maintype() == 'multipart':
            continue
        # ファイル名の取得
        attach_fname = part.get_filename()
        # ファイル名がない場合は本文のはず
        if not attach_fname:
            charset = str(part.get_content_charset())
            if charset:
                body += part.get_payload(decode=True).decode(charset, errors="replace")
            else:
                body += part.get_payload(decode=True)
        else:
            # ファイル名があるならそれは添付ファイルなので
            # データを取得する
            attach_file_list.append({
                "name": attach_fname,
                "data": part.get_payload(decode=True)
            })
    print(body)
    
    TOPIC_ARN = os.environ['TOPIC_ARN']
 
    client = boto3.client('sns')
     
    response = client.publish(
        TopicArn = TOPIC_ARN,
        Message = f'To:{email_header["To"]}\nFrom:{email_header["From"]}\nDate:{email_header["Date"]}\nbody',
        Subject = f'メール受信:{email_header["Subject"]}'
    )

    return {
        'statusCode': 200
    }

送信メール

テストメールを送信してみる

タイトル
本文

うん。いい感じ。

続きは明日。


るな
るな
エンジニア