Google App Script 投資 自動化

【GAS】楽天証券投信積立予定メールから積立予定日をカレンダー登録

はじめに

まず、ご存じの通り、楽天証券では投信積立予定日をメールで連絡してくれます。

そして、この投信積立予定メールの内容をGoogle Apps Scriptを使用してカレンダーに自動登録することを考えます。

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

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

対象読者

  • 楽天証券で投信積立を行っている
  • 楽天証券からの投信積立案内メールをGmailで受信している
  • 投信積立予定をGoogleカレンダーに自動登録させたい

では、詳細を見ていきましょう。

投信積立予定日をGoogleカレンダーに自動登録する

まず、ほとんどの処理はこれまで実装してきたAmazon注文情報処理や楽天注文情報処理と同様です。

もし、この記事を初めて見た場合は、以下のまとめ記事を確認することをお勧めします。

これまで紹介した処理との主な違いは以下の3点です。

  • メール検索文字が楽天証券からの次回積立予定の案内である
  • メール本文内容解析が、積立予定内容を抽出している
  • カレンダーに登録する文字が「投信積立予定」である

次項から、順番に処理を見ていきましょう。

処理概要

まず、以下の処理を実装することを考えます。

  • 楽天証券からの投信積立予定メールを受信
  • 未処理のメールを抽出
  • 投信積立予定日をカレンダーに登録
  • 処理したメールに処理済みラベルを付ける

そして、この処理を定期的に実行するようにします。

未処理メールの抽出

最初に考える処理は、対象メールの抽出処理です。

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

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

// GMail検索文字列(楽天証券からの投信積立予定メール)
var SEARCH_QUERY = 'is:unread -label:' + LABEL +' from:service@rakuten-sec.co.jp subject:【投資信託】次回積立予定をお知らせします';

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

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

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

  • 未読であること(is:unread)
  • 「自動処理済」のラベルが付いていないこと(-label:自動処理済)
  • 楽天証券からのメールであること(from:service@rakuten-sec.co.jp)
  • 件名から投資信託積立予定のメールであることが確認できること(subject:【投資信託】次回積立予定をお知らせします)

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

イベント内容説明

次に考える処理は、カレンダーにイベントとして登録する処理です。

カレンダーには、以下の画像のように積立内容が確認できるように積立の内訳を登録します。

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

  • メールのプレーンテキストをmessage.getPlainBody()で取得してbodyに格納し、そこから改行で区切った本文をrowsに格納し、その後1行ごとに処理します。
  • 「積立予定日」の文字列を見つけたら、scheduleに積立予定日を格納します。これが登録するイベントの日付になります。
  • イベントに登録する内容は、PICK_STARTで指定される文字から、PICK_ENDで指定される文字までにある内容とします。
  • 同じ日にちで同じタイトルのイベントがないかチェック後にイベントを登録します。
  • イベントタイトルは、「投信積立予定」としています。
  • イベントの色はCalendarApp.EventColor.REDで指定する赤色にしています。
  • カレンダーに登録後、メールに処理が完了したことを示すラベル「自動処理済」をputLabel()で付けます。
// 検索文字列
var CONTRACT_SCHEDULE = '積立予定日 ';
var PICK_START = '対象の積立設定';
var PICK_END = '「楽天カードクレジット決済で積立」の取引スケジュールは、楽天証券サイトにてご確認ください。';

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 schedule = null;      // 積立予定日
      var contents = null;      // 登録内容
      var flgAdd = 0;           // 追加フラグ(0:追加しない、1:追加する)

      // 投資信託約定情報メールの内容確認
      var rows = body.split("\n");
      for (var i in rows) {
        var row = rows[i];

        if (row.indexOf(CONTRACT_SCHEDULE) >= 0){
          schedule = row.replace(CONTRACT_SCHEDULE, '');
          schedule = schedule.replace('年', '/');
          schedule = schedule.replace('月', '/');
          schedule = schedule.replace('日', '');
        }
        else if (row.indexOf(PICK_START) >= 0) {
          contents = row;
          flgAdd = 1;
        } 
        else if (row.indexOf(PICK_END) >= 0) {
          flgAdd = 0;
        }

        // PICK_STARTからPICK_ENDの前の行までを取得する
        if(flgAdd){
          if(contents == null){
            contents = row;
          }
          else{
            contents = contents + row + '\n';
          }
        }
      }

      // Google Calendarに登録
      var registerDate = new Date(schedule);
      if (schedule && contents) {
        var title = '投信積立予定';

        // カレンダーに追加
        if(checkConflict(calendar, registerDate, title) == false){
          var options = {
            description : contents
          }
          var event = calendar.createAllDayEvent(title, registerDate, options); 
          event.setColor(CalendarApp.EventColor.PALE_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で指定されるラベルが存在しない場合は作成する
 */

// 処理済み後に付けるラベル名(ラベルが存在しなければ自動的に作られる)
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に以下のフォーマットで終日イベントが作成される。
 * 「投信積立予定」
 * 
 * 2021/09/15 Created by N.Sekiya
 */

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

// GMail検索文字列(楽天証券からの投信積立予定メール)
var SEARCH_QUERY = 'is:unread -label:' + LABEL +' from:service@rakuten-sec.co.jp subject:【投資信託】次回積立予定をお知らせします';

// 検索文字列
var CONTRACT_SCHEDULE = '積立予定日 ';
var PICK_START = '対象の積立設定';
var PICK_END = '「楽天カードクレジット決済で積立」の取引スケジュールは、楽天証券サイトにてご確認ください。';

function registInvestmentTrustContractScheduleToCalendar() {

  // デフォルトカレンダーを取得
  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の本文を取得する
      //Logger.log("body=" + body);

      var schedule = null;      // 積立予定日
      var contents = null;      // 登録内容
      var flgAdd = 0;           // 追加フラグ(0:追加しない、1:追加する)

      // 投資信託約定情報メールの内容確認
      var rows = body.split("\n");
      for (var i in rows) {
        var row = rows[i];

        if (row.indexOf(CONTRACT_SCHEDULE) >= 0){
          schedule = row.replace(CONTRACT_SCHEDULE, '');
          schedule = schedule.replace('年', '/');
          schedule = schedule.replace('月', '/');
          schedule = schedule.replace('日', '');
        }
        else if (row.indexOf(PICK_START) >= 0) {
          contents = row;
          flgAdd = 1;
        } 
        else if (row.indexOf(PICK_END) >= 0) {
          flgAdd = 0;
        }

        // PICK_STARTからPICK_ENDの前の行までを取得する
        if(flgAdd){
          if(contents == null){
            contents = row;
          }
          else{
            contents = contents + row + '\n';
          }
        }
      }
      //Logger.log("schedule=" + schedule);
      //Logger.log("contents=" + contents);

      // Google Calendarに登録
      var registerDate = new Date(schedule);
      if (schedule && contents) {
        var title = '投信積立予定';

        // カレンダーに追加
        if(checkConflict(calendar, registerDate, title) == false){
          var options = {
            description : contents
          }
          var event = calendar.createAllDayEvent(title, registerDate, options); 
          event.setColor(CalendarApp.EventColor.PALE_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分おきにします。

まとめ

最後に、投信積立予定は、設定したら自動的に積立が続くため、定期的に積立内容をチェックしないと忘れてしまうことがあると思います。

ここでの処理を実装することにより、いつ、なにを、どれだけ積み立てるのかをカレンダーに登録し、他のスケジュールと共にチェックすることが可能となります。

情報の一元化は、効率向上の一つの手段だと思います。

いろいろ見て回るのは手間ですからね。

ここで紹介した処理が、みなさまにとって参考になれば嬉しく思います。

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

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