SendGridからbounceメールを取得して、sisimaiで解析してみる
この記事は ユアマイスター AdventCalendar2021 の21日目の記事です。
こんにちは Itsuoです。2021年11月から ユアマイスターにjoinしました。
バウンスメールを解析したい
メールの配信時に一番困るのは、登録されたメールアドレスがbounceしてしまうこと。
特にキャリアがdocomoのユーザーがドメイン許可していない場合でも、帰ってくるbounceメールには 'User Unknown'と表記されていること。
なにかこれを解析する手立て無いかと色々と見ていたら、sisimaiというライブラリがあるので、こちらでなんとかできないものかと試してみました。
※ユアマイスターでは SendGridを使用していますが、正確にはSMTP Apiを使用しているので今回のものには、当てはまらないのですが、テストとしてやってみたいと思います。
(先に言っておきますが、友達がすくない私は周りにドコモの人がいなかったので、ドコモでの検証はできていません、、涙)
2021/12/23追記
中の方からすべてが転送されるわけではない
とのご指摘いただきました。
そこのところは boundce Apiとか使って整合性をあわせないといけないですね
SendGridで配信していることを想定し、Inbound Parseを利用して、boundメールを取得・解析してみたいと思います。
以下が今回の流れです
※実際にはメール発出サーバとInbound Parseの受け取りは同じサーバの必要はありませんがここでは便宜的に同じにします。
boundメール受け取る準備(SendGrid編)
Inbound Parseの設定
Settings -> Inbound Parseをクリック
Receiving Domainの subdomain
には 今回は"parse"とします。
(parse.認証済み設定のドメイン
はDNSにてmxレコードを設定しています)
Domain
にはすでに認証済みのドメインがあるので、それを選択します。
Destination URL
にはPOSTにて送信されるJsonをパースするスクリプトのURLを貼ります。
(ここいら辺の設定は他のやっていると思うので割愛)
これにより parse.認証済み設定のドメイン(例 parse.example.com)にくるメールはすべてDestination URL
に設定されたURLにJSON形式で飛んできます。
boundメールを転送する設定
はじめは メールのreply_toとかに先程のparse.example.com
を設定すれば飛んでくるかと思ったのですが、待てど暮らせど来ない。ドキュメントをよく読むとbounceメールの宛先である Return-Path
はSendGridで生成しているので設定できないとのこと。
頓挫か?と思ったのですが、ちゃんと転送してくれる設定項目がありました。
Setting -> Mail Setting -> Forward Bounce Messagesをクリック
Specify email address
に先程のInbound Parseのメアドを設定します。
今回は parse@parse.example.comとします
(ドメインがあっていれば@の前は英単語だったら良いみたいです)
Forward Bounce Messages Status
を ENABLEDに
これでSendGrid側の準備は整いました。
boundメール受け取る準備(サーバ編)
今回は手持ちのサーバではphpとrubyが使えるのですが、railsに対応していないので phpからメールを書き出し、exec
でrubyスクリプトを呼び出すようにします。
ドキュメントを見るとメールの本体はEmail
というプロパティのようです(実際はemail
でしたが、、)
$json = file_get_contents("php://input");
if(isset($json)){
$content = json_decode($json,true);
if(isset($content['email'])){
$date = new DateTime();
$filename = "mail_{$date->getTimestamp()}.txt";
$fp = fopen($filename, "w");
if($fp){
if( flock($fp, LOCK_EX)){
$status = fwrite($fp,$content['email']);
if( $status ){
// ruby にわたす
exec("ruby parse.rb $filename",$output, $result);
}else{
header('HTTP/1.1 403');
}
}
}
}
}
require("sisimai")
filename = ARGV[0]
# メールの解析
resultArray = Sisimai.make(filename)
if resultArray.is_a? Array
result = resultArray[0]
resultFilename = File.basename(filename,".txt")+"_result.txt"
File.open(resultFilename,"w") do |f|
f.puts("result => " + result.reason)
end
end
Sisimai.make
に入れればテキストでもfileでも解析してくれるところがrubyっぽいですね。
動かしてみる
メールを送信してます。
const sgMail = require('@sendgrid/mail')
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
const msg = {
to: 'no-user@test-example.com',
from: 'reply@example.com',
subject: 'Sending with SendGrid is Fun',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
}
sgMail
.send(msg)
.then(() => {
console.log('Email sent')
})
.catch((error) => {
console.error(error)
})
(実際には example.comはSendGridで認証したドメインです)
ここいら辺は SendGridのチュートリアルから抜粋
送信から結構タイムラグが発生しますが、メールは指定した parse@paser.example.comに転送され、Inbound Parseにて サーバに飛んできました。
Received: by xxxxxx.sendgrid.net with SMTP id xxxxxxx Mon, 20 Dec 2021 15:45:06 +0000 (UTC)
Received: from xxxxxx.outbound-mail.sendgrid.net (unknown [50.31.45.1]) by mx0139p1mdw1.sendgrid.net (Postfix) with ESMTPS id 8CA314614E9 for <reply@parse.millonmillon.com>; Mon, 20 Dec 2021 15:45:06 +0000 (UTC)
MIME-Version: 1.0........
result=>userunknown
SendGridはPostfixを使っているのですね。
まあ、最初にも書きましたが、docomoでの検証はできていませんが、思っていたことはできました。
感想
やっている途中でなんだかやってみたら回り道をしているような気がしましたが、一応解析まではできました。
さらっとやっているようですが、借りているレンタルサーバが bundlerが入っていなく、gemのインストールやrequireがうまく行かなかったり、apacheのエラーログが取れなかったりと結構時間を費やしてしまいました。
sisimai自体は結構良さそうなライブラリなので、Docomoの検証がうまいいったら何かしらに使いたいですね。(だれかちょっとだけでいいので使わせて、、)
次期アップデートではGoに移植されるそうです。
(いやNode.jsかRustに移植お願いします)
Discussion
中の人から「全部が転送できるわけではない」って教えてもらいました。ご参考になれば幸いです。
連絡 ありがとうございます!