Bash から Gmail API でメール送信


はじめに

クラウド上で稼働しているシステムがあり、シェルスクリプトにて稼働監視を行っており、問題が発生した場合にGmailを利用してメール通知を行っています。
2019年10月よりSMTP接続でのGmailの利用が安全性の低いアプリ扱いになり、「安全性の低いアプリのアクセス」を有効にしなくてはならなくなりました。有効にした場合でも、長期間に渡ってメール送信を行わなかった場合、自動的に「安全性の低いアプリのアクセス」が無効になってしまい、メールが送信されなくなってしまいます。
稼働監視にて極稀にしか発生しないシステムエラーでメールが送信されず、ちょっと問題となったため、対応としてGmail APIを利用することとしました。
SMTPでOauth2認証もありかなとは思いましたが、いろいろとめんどくさそうなので…
Gmail APIの設定方法や高級言語での利用方法はあちこちに情報がありますが、シェルスクリプトでの事例が見つからなかったので、メモとして情報を共有します。

前提事項

  • Google側の設定
    • Google Developpers Consoleにてプロジェクトを作成していること
    • APIと認証でGmail APIがONになっていること
    • OAuthクライアントIDが作成されていること
    • アクセストークンを取得していること

上記については他に様々な記事があるので割愛します。

  • パッケージ
    • nkf、jq、curl、tr、base64 など

リフレッシュトークンを元にアクセストークンを取得

GMAIL APIを叩くに先立ち、アクセストークンを取得する必要があります。アクセストークンは一度発行されても30分で期限が切れてしまうため、先に更新しておく必要があります。

アクセストークン取得
REFRESH_TOKEN="<リフレッシュトークン>"
CLIENT_ID="<クライアントID>"
CLIENT_SECRET="<クライアントシークレット>"
ACCESS_TOKEN=$(curl -s -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d "refresh_token=$REFRESH_TOKEN" -d "grant_type=refresh_token" https://accounts.google.com/o/oauth2/token | jq '.access_token' | tr -d '"')

メールサブジェクトのエンコード

メールのサブジェクトのエンコードはいろいろとめんどくさいのでnkfにおまかせです。下記はSUBJECT_NONENCODEに代入した文字列をnkfにてエンコードしている例です。

サブジェクトのエンコード
SUBJECT_NONENCODE="ほげほげ"
SUBJECT=$(echo $SUBJECT_NONENCODE | nkf -W -M -w)

本文の作成(メール宛先を含む)

GMail APIを利用する場合、メール宛先を含んだメール本文をBase64URLエンコードし、生成した文字列をJSON文字列に加工しておく必要があります。下記はMESSAGE_FROMに送信元のメールアドレス、MAIL_TO、MAIL_CC、MAIL_BCCにそれぞれ送信先のメールアドレスが代入され、MAIL_TEXTに本文が代入されている前提で、JSON文字列を作成する例です。
MAIL_BODYにメール本文、MAIL_BODY_ENCODEにBase64URLエンコードしたメール本文、MESSAGE_PARTにJSON化したメール本文を代入しています。

サブジェクトのエンコード
LF="
"
MAIL_BODY="Content-type: text/plain; charset=utf-8
MIME-Version: 1.0
From: $MESSAGE_FROM
Content-Transfer-Encoding: 7bit
Subject: $SUBJECT
To: $MAIL_TO
"
if [ "$MAIL_CC" != "" ]; then
        MAIL_BODY=$MAIL_BODY"Cc: $MAIL_CC"$LF
fi
if [ "$MAIL_BCC" != "" ]; then
        MAIL_BODY=$MAIL_BODY"Bcc: $MAIL_BCC"$LF
fi
MAIL_BODY=$MAIL_BODY$LF$MAIL_TEXT
MAIL_BODY_ENCODE=$(echo "$MAIL_BODY" | base64 | tr '/+' '_-' | tr -d '=\n')
MESSAGE_PART="{\"raw\": \"$MAIL_BODY_ENCODE\"}"

メール送信

作成したJSON文字列をGmail APIで送信します。RESPONSEに送信が成功するとRETCODEにSENTが代入されます。

メール送信
RESPONSE=$(curl -s -X POST -H "Content-type: application/json" -H "Accept: application/json" -H "Authorization: Bearer $ACCESS_TOKEN" -d "$MESSAGE_PART" https://gmail.googleapis.com/gmail/v1/users/me/messages/send)
RETCODE=$(echo "$RESPONSE" | jq '.labelIds[0]' | tr -d '"')

まとめ

Gmail APIを利用することで、確実にシステムエラーメールを送信できるようになりました。