Google App Script 自動化

【GAP】楽天カード請求予定額をカレンダーに自動登録

はじめに

Amazon、楽天市場、Google Payの決済を楽天カードで行っているので、請求予定額がカレンダーの引き落とし日に自動的に登録されると、確認する手間が減って嬉しいなあと思い作りました。

楽天カードは請求予定額についての案内が毎月メールで来るので、Google Apps Scriptを使用してカレンダーに自動登録することを考えます。

Google App Scriptについては以下の記事を参照ください。

ここで紹介する内容は、以下の条件を満たす方が対象となります。

対象読者

  • 楽天カードを使用している
  • 楽天カードからの請求金額案内をGmailで受信している
  • 楽天カードからの請求金額案内をGoogleカレンダーに自動登録させたい

では、実装内容を見ていきましょう。

楽天カードは関谷さんおすすめのカードなので、ちょっぴり宣伝させてください。持っていない方は、この機会に検討されても良いのではないでしょうか。

楽天カードの請求額をカレンダー登録

ほとんどの処理はAmazon注文情報処理や楽天注文情報処理と同様です。

主な違いは以下の3点です。

  • メール検索文字が楽天カードからの請求金額案内のメールを特定している
  • メール本文内容解析が、請求金額の抽出になる
  • カレンダーに登録する文字が「楽天カード+請求予定額」である

処理概要

以下のような処理を実装することを考えます。

  • 楽天カードから請求予定額確認メールを受信
  • 未処理のメールを抽出
  • カレンダーの引落日に請求予定額を登録
  • 請求予定額確認メールに処理済みラベルを付ける

そして、この処理を定期的に行わせます。

未処理メールの抽出

楽天市場からの注文確認メールで、未処理のメールの抽出は以下の検索文字列で実現します。

メールの抽出条件の構文は、Gmailの抽出条件に設定できる構文と同じです。

// 処理済み後に付けるラベル名
var LABEL = '自動処理済';

// GMail検索文字列(楽天カードからの請求予定金額情報)
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:info@mail.rakuten-card.co.jp subject:【楽天カード】ご請求予定金額のご案内 ';

処理済みを識別するために、処理完了後に該当メールのラベルに「自動処理済」を付けます。

そのため、抽出条件は以下の4つとなります。

  • 未読であること(is:unread)
  • 「自動処理済」のラベルが付いていないこと(-label:自動処理済)
  • 楽天市場からのメールであること(from:info@mail.rakuten-card.co.jp)
  • 注文確認メールが確認できること(subject:【楽天カード】ご請求予定金額のご案内)

Gmailへのアクセス方法については以下の記事を参照ください。

イベント内容説明

カレンダーには以下のように登録されます。

請求金額がすぐに確認できるように、イベントのタイトルに金額を入れます。

以下に処理概要とコードの抜粋を示します。

  • メールのプレーンテキストをmessage.getPlainBody()で取得してbodyに格納し、そこから改行で区切った本文をrowsに格納し、その後1行ごとに処理します。
  • 「【お支払日】」の文字列を見つけたら、payDayに支払日を格納します。payDayがイベントに登録する日となります。
  • 「【ご請求予定金額】」の文字列を見つけたら、payAmountに請求予定額を格納します。
  • 登録するイベントは終日イベントとします。
  • 同じ日にちで同じタイトルのイベントがないかチェック後にイベントを登録します。
  • イベントタイトルは、「楽天カード+請求額」としています。
  • イベントの色はCalendarApp.EventColor.REDで指定する赤色にしています。
  • カレンダーに登録後、メールに処理が完了したことを示すラベル「自動処理済」をputLabel()で付けます。
// 検索文字列
var PAY_DAY = "【お支払い日】   ";
var PAY_AMOUNT = "【ご請求予定金額】 ";

for(var k in threads){
  var thread = threads[k]
  var messages = thread.getMessages();
  for (var j in messages){
    var message = messages[j];
    var body = message.getPlainBody();    // HtmlメールからPlain textの本文を取得する
    var payDay = null;        // 支払い日
    var payAmount = null;     // 支払金額

    // 楽天カードからの支払い情報メールの内容確認
    var rows = body.split("\n");
    for (var i in rows) {
      var row = rows[i];

      if (row.indexOf(PAY_DAY) >= 0) {
        payDay = row.replace(PAY_DAY, "");
      } 
      else if (row.indexOf(PAY_AMOUNT) >= 0) {
        payAmount = row.replace(PAY_AMOUNT, "");
      }
    }

    // Google Calendarに登録
    var registerDate = new Date(payDay);
    if (payDay && payAmount) {
      var title = "楽天カード¥" + payAmount;
      Logger.log("登録内容:" + title);

      // カレンダーに追加
      if(checkConflict(calendar, registerDate, title) == false){
        var event = calendar.createAllDayEvent(title, registerDate);
        event.setColor(CalendarApp.EventColor.RED);
        //event.removeAllReminders();   // 予定なので通知させる
      }

      // 処理済みメールとしてラベルを付ける
      putLabel(thread);  
    }
  }
}

上記コードは説明のために抜粋したものなので、このままでは動作しません。

コードの全体については、「すべてのコード」を確認ください。

登録済み情報チェック

カレンダーに追加する前に、同じ時間の同じタイトルで、既にカレンダーへの登録があるかチェックしています。

そして、同じイベントの登録がない場合に、カレンダーに登録します。

function checkConflict(calendar, registerDate, registerTitle){

  // registerDateで指定する日付のイベントリスト取得
  var events = calendar.getEventsForDay(registerDate);

  // イベントリスト中のイベント毎に登録済みかチェックする
  for (var i in events) {
    var event = events[i];                          // イベントひとつ取得
    var eventTitle = event.getTitle();              // イベントタイトル取得
    if(event.isAllDayEvent()){                      // 終日イベントかチェック
      if(eventTitle.indexOf(registerTitle) >= 0) {  // イベント登録済みかチェック
        Logger.log("登録済:event.getTitle()=" + event.getTitle() + "registerTitle=" + registerTitle);

        // イベント登録済み
        return true;
      }
    }
  }
  // イベント未登録
  return false;
}

この関数では、カレンダーへの登録があるかをチェックし、以下のように値を返します。

  • イベント登録済み:true
  • イベント未登録:false

処理済みラベル

処理済みを識別する方法としては、既読にする方法もあるのですが、それだとメールをチェックした時に気づくことができず見落とす可能性が高くなるため、処理済みメールの識別には「自動処理済」のラベルを付けることにします。

この「自動処理済」のラベルについては、好みで変更することができます。

var LABEL = '自動処理済';

function putLabel(thread){
  var label = GmailApp.getUserLabelByName(LABEL);
  if(label){
    thread.addLabel(label);
    Logger.log('既存ラベル使用');
  }
  else{
    var newlabel = GmailApp.createLabel(LABEL);
    thread.addLabel(newlabel);
    Logger.log('新規ラベル追加');
  }
}

「自動処理済」のラベルがない場合は、新規にラベルを作る仕組みとしています。

実行結果

Googleカレンダーには以下のように登録されます。

イベントをクリックすることで、詳細内容を確認できますが、今回のスクリプトでは何も登録していません。

ちょっと実装のハードルは高そうですが、イベントの詳細内容にカード使用内訳を登録できると良いかもしれませんね。

全てのコード

/*
 * 楽天カードからの請求予定金額情報をGoogle Calendarに登録する
 * 
 * Google Calenderに以下のフォーマットで終日イベントが作成される。
 * 「楽天カード¥xxx,xxx円」
 * 
 * 2021/09/13 Created by N.Sekiya
 */

// 処理済み後に付けるラベル名(ラベルが存在しなければ自動的に作られる)
var LABEL = '自動処理済';

// GMail検索文字列(楽天カードからの請求予定金額情報)
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:info@mail.rakuten-card.co.jp subject:【楽天カード】ご請求予定金額のご案内 ';

// 検索文字列
var PAY_DAY = "【お支払い日】   ";
var PAY_AMOUNT = "【ご請求予定金額】 ";

function registPaymentInformationToCalendar() {

  // デフォルトカレンダーを取得
  var calendar = CalendarApp.getDefaultCalendar();

  var threads = GmailApp.search(SEARCH_QUERY, 0, 1);
  if (threads.length === 0) {
    Logger.log("メールが見つかりません");
    return;
  }

  for(var k in threads){
    var thread = threads[k]
    var messages = thread.getMessages();
    for (var j in messages){
      var message = messages[j];
      var body = message.getPlainBody();    // HtmlメールからPlain textの本文を取得する
      var payDay = null;        // 支払い日
      var payAmount = null;     // 支払金額

      // 楽天カードからの支払い情報メールの内容確認
      var rows = body.split("\n");
      for (var i in rows) {
        var row = rows[i];

        if (row.indexOf(PAY_DAY) >= 0) {
          payDay = row.replace(PAY_DAY, "");
        } 
        else if (row.indexOf(PAY_AMOUNT) >= 0) {
          payAmount = row.replace(PAY_AMOUNT, "");
        }
      }

      // Google Calendarに登録
      var registerDate = new Date(payDay);
      if (payDay && payAmount) {
        var title = "楽天カード¥" + payAmount;
        Logger.log("登録内容:" + title);

        // カレンダーに追加
        if(checkConflict(calendar, registerDate, title) == false){
          var event = calendar.createAllDayEvent(title, registerDate);
          event.setColor(CalendarApp.EventColor.RED);
          //event.removeAllReminders();   // 予定なので通知させる
        }

        // 処理済みメールとしてラベルを付ける
        putLabel(thread);  
      }
    }
  }
}

/*
 * 登録済みの配当金支払日情報があるかチェック
 */
function checkConflict(calendar, registerDate, registerTitle){

  // registerDateで指定する日付のイベントリスト取得
  var events = calendar.getEventsForDay(registerDate);

  // イベントリスト中のイベント毎に登録済みかチェックする
  for (var i in events) {
    var event = events[i];                          // イベントひとつ取得
    var eventTitle = event.getTitle();              // イベントタイトル取得
    if(event.isAllDayEvent()){                      // 終日イベントかチェック
      if(eventTitle.indexOf(registerTitle) >= 0) {  // イベント登録済みかチェック
        Logger.log("登録済:event.getTitle()=" + event.getTitle() + "registerTitle=" + registerTitle);

        // イベント登録済み
        return true;
      }
    }
  }
  // イベント未登録
  return false;
}

/*
 * 処理済みとしてスレッドにラベルを付ける
 * 
 * LABELで指定されるラベルが存在しない場合は作成する
 */
function putLabel(thread){
  var label = GmailApp.getUserLabelByName(LABEL);
  if(label){
    thread.addLabel(label);
    Logger.log('既存ラベル使用');
  }
  else{
    var newlabel = GmailApp.createLabel(LABEL);
    thread.addLabel(newlabel);
    Logger.log('新規ラベル追加');
  }
}

繰返し実行

正しく動作することを確認したら、繰り返し実行できるよう設定します。

繰り返し実行には、トリガーの設定を行います。設定方法については、以下の記事を参照ください。

繰り返し実行する間隔は、急ぐ必要のない処理なので5分おきにします。

最後に

イベントに登録する際に、アラームの設定はデフォルトを採用していますが、口座残高の確認と必要な場合に入金処理をしなければならないことを考えると、1日か2日まえにアラームを設定するように変更しても良いと思います。難しい処理ではないのでお試しください。

また、たいていのクレジットカードは、引落日と請求予定額の案内をメールで通知しているかと思います。

関谷さんの保有カード楽天カードだけなので、残念ながら他のカードの案内についてはわかりません。もし、楽天カード以外の請求予定額通知メールの内容をカレンダーに表示させたい方がいらしたら、気軽に相談してください。

では、今日も良い一日を。

-Google App Script, 自動化
-, , , , , ,