2015年5月29日金曜日

Amazon SESのバウンスをSNSで受けてSQSに渡しアプリで拾うまで

Webアプリを作っていたら、メールサーバがないことに気付いた。以前の会社では誰ぞに「メールサーバはどれを使えばいいですか?」と聞いておけば出来てたのに。

頭来たので適当にSMTPサーバ?今もSedMailとかでいいの?を立てようかと思ったのは一瞬で、スパム対策が進んだ昨今、10年前のように適当にサーバを設置してパカパカ送ってたら問題になるというか止められるだろう、と。

そう思って、Amazon SESを使ってみようと思ったが、バウンスメールを出すと止められるから嫌だと上司に言われた・・・が、SESでなきゃISPに止められたりするのであって結局どこを使うのであってもバウンス対策は必要。

バウンス対策ごと丸投げできるメール配信サービスは、今回の自分のようにメールテンプレートに大量の変数を使うような場面ではキツい気がするし。

ということで、SESでバウンス対策だけはある程度やってみるという覚悟を決め、ある程度試してみた作業ログ。


※ 適当にググっていろいろ見た結果を適当に混ぜてやった感じなので、まともに調べてる方は他所を見た方が正確だと思われます。


準備

  1.  送り元になるアドレスを最初に登録して、Verifyしないといけない。(登録アドレスに送られてきたメールをクリックする)
  2.  これでとりあえず登録アドレスから登録アドレス向けにだけメールが送れる。
    1.  このアドレスでは不達のテストとかは不可能。なので、テストアドレスがある。 (ドキュメンテーション Eメール送信のテスト) http://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/mailbox-simulator.html

バウンス 


  1. バウンスはAmazon SNSで受け取る。
  2. SESのコンソールのEmail Addresses、アドレスを選択して上部からView Detailsを選ぶ。さらにNotrificationsを開き、さらにボタンを押すと通知の設定ができる
  3. Topicを作る的リンクが地味にあるのでそれを押してTopicを生成する→SESのリージョンでSNSのTopicが出来てる
    1.  AWSのダッシュボードで別途に作っておいたTopicは選択できなかった。なんぞ?→リージョンが違った。SESのリージョンに合わせないとダメ
  4.  Amazon SNSのダッシュボードに移動、Subscriptionsで、バウンス発生時のアクションを決める。仮にEメールにしてみた。設定したアドレスに購読Verifyメールが来るのでOKする。
  5. bounce@でテストメールを送る(SESコンソールから)
  6. バウンス情報がJSONになってメール来た



が、メールじゃバウンスを処理できない。・・・こともないが、やりにくい。こちらのシステムもずっと動いているわけじゃないので、IMAPで取ってきて「まだ見てないの」を開封してみて・・・というあたりが面倒になりそう。


そこで、Amazon SQSを使う。

Amazon SQSでSNSメッセージを受け取る設定


  1. Amazon SQSで、キューを作る。名前は"SESBounce"とか。
  2. SNSのサブスクリプションにエンドポイントをSQSにしたのを作り、さきほどのキューを指定。
  3. ところがバウンス来ない。
  4. キューのダッシュボードに戻り、作成済みのキューに「キュー操作」ボタンでSNSのサブスクリプションを設定
  5. SESからbounce@にテストメールを送ると、キューに1つタスクがたまった



キューからタスクを取り出さなくてはならない。これはバッチで良い気がする。

Amazon SDKを使う。

「gradle aws sdk」とかググってMavenリポジトリに行き、IDを取得、GradleでSDKをインポート。zipでも100MB超。デカい!

SDKでSQSに接続する


  1. まず、認証情報が必要。AWSコンソールのIAM(Identity and Access Management )を開き、左「ユーザー」メニューで新規ユーザー追加。名前だけ入れる。と生成され、アクセスキーとシークレットキーが出てくる。
  2.  このキーを使ってAPIで接続できるが、作ったユーザーには何の権限もないので接続拒否される。IAMのユーザー設定画面で「ポリシーのアタッチ」をして利用する機能を許可する。
  3. 次のようなコードを書いて実行する。


まあ、センスのないコードだが手順的なものとして・・・。

注意:なぜか、キー"Message"の値のJSONがわざわざエスケープして文字列にしてあるので、一度文字列として取り出してから再度JSONとしてパースしている。その中のJSONはそのままだったりするのに不思議だ。

static void testQue(){
final String accessKey = "XXXXXXXXXXXXXXX";
final String secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
AmazonSQSClient sqsc = new AmazonSQSClient(new BasicAWSCredentials(accessKey, secretKey));
//ReceiveするとSQS上のメッセージがデフォルト30秒、他からロックされる
//SQSのコンソールで確認できるキューのURL
ReceiveMessageResult receiveMessage = sqsc.receiveMessage("https://sqs.us-east-1.amazonaws.com/yyyyyyyyyyyyy/SESBounce");
receiveMessage.getMessages().forEach((msg)->{
System.out.println("=======================================");
String body = msg.getBody();
JSON json = JSON.of(body);
System.out.println("Bounced:");
try{
JSON message = JSON.of(json.getPlainString("Message"));
for(JSON bnc : message.getJSON("bounce").getJSONs("bouncedRecipients")){
System.out.println("emailAddress : " + bnc.getString("emailAddress"));
System.out.println("status : " + bnc.getString("status"));
System.out.println("diagnosticCode : " + bnc.getString("diagnosticCode"));
}
//message.getJSONs("bouncedRecipients").forEach((bnc)->System.out.println(bnc.getString("emailAddress")));
}catch(Exception ig){
ig.printStackTrace();
}
System.out.println("=======================================");
});
}

実行したらコンソールにバウンスしたアドレスと理由が出た。ここまでくれば、後はこれをリストにしておいてメール送らないようにすればいいのだろう、と。


AWSってなんだか面白い気もしてきた。

0 件のコメント:

コメントを投稿