S3へ保管したメールをパースしてDynamoDBへ入れてみる


前回記事AWS SESでメールを受信してS3へ保管してみる の続きです。

前回はSESで受信したメールをS3へ保管するところまでやりました。
今回はS3に保管したメールを読んでみたいと思います。

メールの中身

[メールヘッダ省略]
Content-Type: text/plain; charset="iso-2022-jp"

Content-Transfer-Encoding: 7bit

MIME-Version: 1.0

X-PHP-Qdmail: version-1.2.6b The_MIT_License http://hal456.net/qdmail PHPver 5.2.17

    send-by MailFunction

$BEl6h!!9aDG>HMU!!#3CzL\#4HVIU6a$GL}O31L$N$?$a>CKIBb$,=PF0$7$F$$$^$9!#(B
----------------------
$B!~G[?.%(%j%"$N>\:Y@_Dj$dG[?.;~4VBS$NJQ99!"$*$h$SG[?.$N2r=|$O$3$A$i"-(B
http://m119.city.fukuoka.lg.jp

$B!~J!2,;T>CKI6I(B
$B!|7HBS$+$i$N%"%/%;%9$O$3$A$i"-(B
http://119.city.fukuoka.lg.jp/i/

$B!|%Q%=%3%s$+$i$N%"%/%;%9$O$3$A$i"-(B
http://119.city.fukuoka.lg.jp/

実際にSESで受け取ったメールはこんな感じになります。

文字コードがiso-2022-jpなのでよくわかりません。
内容は福岡市消防情報メール(ふくしょうめーる)で、福岡市内の区を指定しておくと、その区に消防車や救急車が出動した場合メールが届くというやつです。
今回はこのメールをパースしてDynamoDBへ保管したいと思います。

S3のイベント追加

S3には、ファイルが追加や削除をされた場合、特定のLambdaを呼び出すという超便利機能があるので、それを利用します。

S3のバケットのプロパティから「イベント」の設定を行います。

今回はこのバケットに何かしらのファイルが作成された場合[ses]というLambdaを呼ぶように設定しました。

プレフィックスやサフィックスを指定して、特定の条件のファイルの作動させることも可能です。

LambdaでS3のファイルを取ってくる

S3からの通知でLambdaが起動した場合、バケット名とファイルKEYを教えてくれます。
Lambdaのコードとしてはこんな感じでS3の内容を取得できます。

exports.handler = (event, context, callback) => {
  var bucket = event.Records[0].s3.bucket.name;
  var key = event.Records[0].s3.object.key;
  s3.getObject({
    Bucket:bucket,
    Key:key
  },function(err,data) {
    //S3に上がった内容がdataに入る
  });
};

文字コード

Node.jsで文字コードの自動判別と自動変換という方法を使うと自動で判別してくれるようですが、今回はiso-2022-jpのメールしかこないのでAWS Lambda内で文字コードを変換する方法に載っていたちょっと荒技っぽい事を使いました。(すごいなこれ)

var filepath = "/tmp/filepath";
fs.writeFileSync(filepath, "変換元文字列");
var exec = require('child_process').exec;
var cmd = "iconv -f iso-2022-jp -t utf-8 " + filepath;
var child = exec(cmd, function(err, stdout, stderr) {
  # stdout <= 変換後文字列
});

tmpに変換元文字列を書き込んで、コマンド実行しちゃうんですね・・Lambdaにこんな使い方があったとは(いいのかな?)
※AWS側のLambda実行環境によっては多分動かなくなる

正規表現で欲しいデータを取り出しDynamoDBに突っ込む

どこの区のどこの街のどこの番地で何のために出動したかを正規表現で取った後、UnixtimeをつけてDynamoDBへPutItemしました。

var tableName = "Dispatches";
if (stdout.match(/(博多区|中央区|南区|西区|早良区|城南区|東区)\s(.+)\s(.+)付近で(.+)のため/g)) {
  var params = {
    "ward" : RegExp.$1,
    "town" : RegExp.$2,
    "street" : RegExp.$3,
    "type" : RegExp.$4,
    "time" : String(date.getTime())
  };
  var dynamoRequest = {
      "TableName" : tableName
  };
  dynamoRequest.Item = marshaler.marshalItem(params);
  dynamo.putItem(dynamoRequest, function (err, data) {
      if (err) {
          console.log('err:', JSON.stringify(err, null, 2));
          context.fail(new Error('putItem query error occured'+ JSON.stringify(dynamoRequest, null, 2)));
      } else {
          context.succeed(data);
      }
  });
}

DynamoDB

無事保管された。

lambdaのコードはGitHubに置いてあります。