バックエンド兼Androidアプリエンジニアの高橋です。
弊社のシステムの一部に、特定のメール(Gmail)を受信したタイミングで、その添付ファイルを使ってバッチ処理を行いたいという要件があります。
Gmailには受信トリガーを設定する事ができないので、GmailのAPIを定期的にポーリングする事で擬似的?に受信を検知したりする方法がありますが、今回はGmailのAPIをポーリングをせずにAWS上で実現する方法をご紹介したいと思います。
全体図
このような構成図となりました。
一つずつ流れを説明していきます。
Gmail → SES
Gmailに来たメールをSESに転送します。
Gmailに受信トリガーが設定できないのであれば、転送先で受信トリガーを設定すればいいという考え方です。
全部のメールを転送すると大変なので、転送フィルターで必要なメールだけを転送するようにします。
転送先のドメインはRoute53でMXレコードを設定します。
値は、画像にあるように「10 inbound-smtp.us-east-1.amazonaws.com」固定です。
SES → S3
次に、SESのコンソール画面でEmail Receiving のRuleSetを作成します。
Recipentには先ほどRoute53に登録したドメインを入力します。(画像は適当です)
なお、この後ドメインの認証のためにTXTレコードが必要になりますが、
Route53を使っていると自動でRoute53に登録してくれるので、道なりに進めればOKです。
その次にSESで受信した時のActionを設定する画面に移ります。
ここでは、受信メールをS3に送ったりLambdaで処理したりする事ができますが、S3に送るようにします。
ここでLambdaに処理を渡してしまうとLambda側でメールの本文が見えないようです。
Rulesetの設定が終わって、Status = EnabledになればOKです。
S3 ⇆ Lambda
最後にLambdaを作ります。
Lambdaの起動トリガーに、SESで設定したバケットの s3:ObjectCreated
を設定します。
また、Lambdaでメールから抽出した添付ファイルをS3に戻すために、適切なIAM Roleをつけてあげる必要があります。
ここら辺の設定はServerless Frameworkを使うと楽です。
作成するLambda関数はこんな感じです。
LambdaはRubyの例です。(サンプルなので動作保証しません。)
def upload_attachment(event:, context:) s3 = Aws::S3::Resource.new bucket_name = event["Records"][0]["s3"]["bucket"]["name"] key = CGI.unescape(event["Records"][0]["s3"]["object"]["key"]) begin # emailをS3からダウンロード tmp_path = '/tmp/' + key s3.bucket(bucket_name) .object(key) .get(response_target: tmp_path) email = Mail.read(tmp_path) # 添付ファイルをS3にアップロード email.attachments.each do | attachment | filename = CGI.unescape(attachment.filename).downcase upload_bucket = s3.bucket(ENV['upload_bucket']) date = Date.today.strftime('%Y%m%d') upload_bucket.object("#{date}/#{filename}").put(body: attachment.decoded) end rescue Exception => e raise e end end
動作確認
ここまでできたら、SESのRecipentに設定したドメインにメールを送ってみて、動作確認します。
添付ファイルがS3のバケットに上がれば目的達成です!
まとめ
今回は、AWSを使ってGmailの受信を検知する方法をご紹介しました。
全体的な流れしか説明していないので、実際に作業する際は他の記事も参照してやってみてください。
細かいところまで書こうとすると、1ページじゃとても書ききれませんでした。。。
Lambdaを作成するところ以外は短時間で構築できると思います。
最後に、弊社LCLではこのようなAWSを活用した仕組みづくりを日々作り続けています。
いろんなアイデアを受け入れたいと思うので、力を貸していただける方を絶賛募集中です!