AWS SESからSNS経由でメール開封やリンククリックを通知する

# 概要
SES　=> SNS => lambda => slackという構成で
メールのバウンス検知だけでなく、メール開封やリンククリックも検知しよう！というお話。
バウンス検知だけの場合とは異なる設定方法を使います。
ちなみにこの設定方法だと、送信メールに特定のヘッダを埋め込むだけで
SES利用の送信メールは全て検知することができます。

# SESでEメールを送信する際にヘッダを付加する
こちらに関してはお使いの言語によって他サイトを参照してください。
Laravelでmailableクラスを使っている場合は、build時に```withSwiftMessage```で
自由にヘッダを設定できます。
つけるヘッダは
```X-SES-CONFIGURATION-SET: {{ #SESで設定するconfig名 }}```

```php
    public function build()
    {
     return $this->view('mail.html.mail')
              ->text('mail.plain.mail')
              ->subject($this->subject)
              ->from($this->from)
              ->withSwiftMessage(function (\Swift_Message $message) {
                  $message->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'test-set-01');
              });
    }
```

# SESでconfigを設定しSNSと連携する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/cd2aa034-18fe-79a4-29de-b71efad14fac.png)
Create Configuration Setをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/6be453a8-21c6-c7a2-aa9b-ac2e85fd516a.png)
Configuration Setの名前を入力。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/8a84faf3-ce12-bfab-4d9d-49d52964c63b.png)
Configuration Setが作成されるので、作成したconfigのリンクをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/4e28d53c-b323-7e1e-925c-3018fe75d345.png)
Configuration Setの編集画面がこちら。
ここで開封やクリックのイベントの出力先を選択する。今回はSNSを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/c4c43847-53ee-03c2-dc66-763d444c4b38.png)
するとこちらのような入力項目になる。
通知したいEvent typesを選択して、連携したいSNS topicを作成するか、
作成済みのSNSを選択するとSES→SNSの連携は完了。
各Event typeの説明はこちら。

#### Event type(AWS公式より)

- Send(送信) 
    - Amazon SESに対する呼び出しが成功しました。Amazon SESはメールの配信を試行します。
- Reject(拒否)
    - Amazon SESはEメールを受け付け、ウイルスが含まれていることを検出した後で拒否しました。Amazon SES は、受取人のメールサーバーにメールの配信を試みませんでした。
- Delivery(配信)
    - Amazon SESが受取人のメールサーバーに Eメールを正常に送信しました。
- Bounce(バウンス) 
    - 受取人のメールサーバーにより、メールは完全に拒否されました。このイベントはハードバウンスに該当します。ソフトバウンスは、Amazon SESが一定期間にわたって再試行してもメールを配信できなかった場合に限ります。
- Complaint(苦情)
    - Eメールは受取人に正常に配信されました。受取人がメールをスパムとしてマークしました。
- Open(オープン)
    - 受取人がメッセージを受け取り、Eメールクライアントで開きました。
- Click(クリック)
    - 受取人はメール内の1つ以上のリンクをクリックしました。
- Rendering failure(レンダリング失敗)
    - テンプレートレンダリングの問題のためにEメールが送信されませんでした。このイベントタイプは、SendTemplatedEmail または SendBulkTemplatedEmail API オペレーションを使用してテンプレートメールを送信する場合にのみ発生します。このイベントタイプは、テンプレートデータが見つからない場合や、テンプレートのパラメーターとデータが一致しない場合に発生します。

#SNSでTOPICを作成
次にSNSでSESから通知を受けられるように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/12cd9de2-b9ea-2ea7-3cf0-f780b1f70e84.png)
トピックを作成。
名前さえ決めれば他はデフォルト値で問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/2fc16bd8-c139-ffd0-72ff-f37b2301e40b.png)
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3331303232392f39613032353038302d323234612d626163622d656139312d3234303162623765643533382e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/f5e2d888-59be-6419-daa7-9a30a220c55e.png)
サブスクリプションではHTTPSでpostしたり、Eメールでの通知も設定できますが、ここでは設定しません。
lambdaから設定した方が簡単なので。
テストしたい場合はとりあえずEメールで通知してみると良いかも。
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3331303232392f31396531376235322d376564382d366363642d636233352d3434316663616135326165612e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310229/3345ede1-fd5d-5068-f76e-5404b4f2b602.png)
#lambdaからSlackに通知
https://qiita.com/icck/items/75f484cd3ccd944cd4c2
こちらを参照のこと。コードはNode.js 10.xでも問題なく動作しました。
```let arr = JSON.parse(rec.Sns.Message);```
した時の```arr.eventType```に各event Typeが入ってくるので、
それによって投稿されるメッセージを変えてあげると分かりやすいです。

# SESで検知できない点
実装して色々テストしてみたのですが、下記の状態取得では一部正確では無い点がありました。

* 携帯のpush通知で開封扱いになる(iOS13で確認)
* gメール, yahooメールで迷惑メール判定されてもComplaintが返ってこない

開封通知については、メール文面に含まれる小さくて透明な画像が表示されたかどうかで判定しているそうです。
AWSのサポートにも問い合わせましたが、メーラーの仕様によるため、これを回避する方法は現状無いそうです。