こんにちは、だあしょ(@daasho_blog)です。
楽天証券で投資信託を積み立てていますが、毎月のどこかで積立予定日を知らせるメールが届きます。
これまでは何気なく見ていたのですが、ふとした時に「せっかくだからGoogleカレンダーに登録したいな」と思いました。
まぁ正直なところ長期投資を目的としているので、放置、意識しない、忘れる、くらいがちょうど良いのかもしれませんが。。。
とはいえ思い立ったが吉日ということで、ちょっと自動化してみます。
普段からGmailやGoogleカレンダーといったGoogleサービスには大変お世話になっています。
そのため、GAS(Google App Script)を使って自動化を目指してみます。
作業履歴と備忘録も兼ねてまとめておきます。
同じように考える人に少しでも参考になれば幸いです。
完成したコード

そんなこんなで試行錯誤しながらも完成したコードがこちらです。
// メール検索の条件を定義(送信元と件名)
var SEARCH_QUERY = 'from:service@rakuten-sec.co.jp subject:【投資信託】次回積立予定をお知らせします';
// メール本文内のキーワードを定義
var KEYWORDS = {
INVESTMENT_DATE: '積立予定日',
STARTING_LINE: '対象の積立設定',
ENDING_LINE: '「楽天キャッシュで積立」の取引スケジュールは、楽天証券サイトにてご確認ください。'
};
function getInvestmentScheduleFromGmail() {
// デフォルトカレンダーを取得
var calendar = CalendarApp.getDefaultCalendar();
// メールを検索する(最新1通)
var threads = GmailApp.search(SEARCH_QUERY, 0, 1);
// メールが見つからない場合の処理
if (threads.length === 0) {
Logger.log('該当するメールが見つかりませんでした。');
return;
}
// 各スレッド内のメッセージを処理
threads.forEach(function(thread) {
thread.getMessages().forEach(function(message) {
var body = message.getPlainBody(); // メール本文の取得(プレーンテキスト)
var lines = body.split("\n"); // 改行で本文を分割
var investmentDate = null; // メール本文から抽出した積立予定日
var investmentDetails = []; // メール本文から抽出した積立内容の詳細
// メール本文から積立予定日を抽出
lines.forEach(function(line) {
if (line.includes(KEYWORDS.INVESTMENT_DATE)) {
investmentDate = line.replace(KEYWORDS.INVESTMENT_DATE, '')
.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日/, '$1/$2/$3');
}
});
// メール本文から積立内容セクションの開始と終了を特定
var startIdx = lines.findIndex(line => line.includes(KEYWORDS.STARTING_LINE));
var endIdx = lines.findIndex(line => line.includes(KEYWORDS.ENDING_LINE));
// 積立内容セクションが存在する場合、内容を抽出
if (startIdx >= 0 && endIdx > startIdx) {
investmentDetails = lines.slice(startIdx + 1, endIdx);
}
// 積立予定日と詳細をログに出力
Logger.log('積立予定日: ' + investmentDate);
Logger.log('積立詳細: ' + investmentDetails.join('\n'));
// 積立予定日が存在し、詳細もある場合のみカレンダーに登録
if (investmentDate && investmentDetails.length > 0) {
// 同じ日付のイベントが存在しないか確認
var events = calendar.getEventsForDay(new Date(investmentDate));
var eventTitle = '投信積立予定';
if (events.some(event => event.getTitle() === eventTitle)) {
Logger.log('同じ日付のイベントが既に存在します。');
} else {
// イベントの作成
var event = calendar.createAllDayEvent(eventTitle, new Date(investmentDate), {
description: investmentDetails.join('\n')
});
event.setColor(CalendarApp.EventColor.PALE_RED); // 色を設定
Logger.log('カレンダーにイベントを追加しました。')
}
}
});
});
}
各行の処理を整理
以下、コードの各行の処理を詳しく説明します。
メール検索の条件を定義
var SEARCH_QUERY = 'from:service@rakuten-sec.co.jp subject:【投資信託】次回積立予定をお知らせします';
検索クエリを定義します。
- from:部分:送信元のメールアドレスを指定します。
- subject:部分:メールの件名に含む文字列を指定します。
この場合、楽天証券からの投信積立予定に関するメールを検索します。
メール本文内のキーワードを定義
メール本文から必要な情報を抽出するためのキーワードを定義します。
var KEYWORDS = {
INVESTMENT_DATE: '積立予定日',
STARTING_LINE: '対象の積立設定',
ENDING_LINE: '「楽天キャッシュで積立」の取引スケジュールは、楽天証券サイトにてご確認ください。'
};
1つずつ定数として定義しても良いですが、項目群がまとまっている方がきれいなので“KEYWORDS”としてまとめておきます。
INVESTMENT_DATE
:積立予定日が記載されている部分を特定するためのキーワード。STARTING_LINE
:積立内容の詳細が始まる部分を示すキーワード。ENDING_LINE
:積立内容の詳細が終わる部分を示すキーワード。
デフォルトカレンダーを取得
Googleカレンダーのデフォルトカレンダーを取得します。
var calendar = CalendarApp.getDefaultCalendar();
カレンダーにイベントを追加するために使用します。
メールを検索する(最新1通)
GmailのAPIを使って、定義した検索クエリ (SEARCH_QUERY) に基づいてメールを検索します。
var threads = GmailApp.search(SEARCH_QUERY, 0, 1);
- 第一引数:検索クエリを指定します。
- 第二引数:検索結果の配列の開始位置を指定します。
- 第三引数:検索結果の配列の終了位置を指定します。
つまり、今回の「0」と「1」は検索結果の最初の1通のみを取得するという意味です。
メールが見つからない場合の処理
検索結果にメールが見つからなかった場合の処理を加えておきます。
if (threads.length === 0) {
Logger.log('該当するメールが見つかりませんでした。');
return;
}
ログに「該当するメールが見つかりませんでした」と記録し、処理を終了させます。
各スレッド内のメッセージを処理
メールスレッド (thread) 内の各メッセージを取得します。
その後、取得した各メッセージに対して処理しています。
threads.forEach(function(thread) {
thread.getMessages().forEach(function(message) {
スレッド内に複数のメッセージがある可能性があるため、すべてのメッセージに対して処理を行います。
メール本文の取得(プレーンテキスト)
メールの本文をプレーンテキスト形式で取得します。
var body = message.getPlainBody();
プレーンテキスト形式とは、HTMLメールなどの形式ではなく、純粋なテキスト形式です。
余計なHTMLなどが入ることで処理がややこしくなることを防ぎます。
改行で本文を分割
メール本文を改行 (\n) で分割し、各行を配列として格納します。
var lines = body.split("\n");
この配列を使って各行に対して処理を行います。
積立予定日と内容を格納する変数を定義
各変数を初期化しておきます。
var investmentDate = null; // メール本文から抽出した積立予定日
var investmentDetails = []; // メール本文から抽出した積立内容の詳細
investmentDate
:メール本文から抽出する積立予定日を格納するための変数。初期値はnull
です。investmentDetails
:積立設定の詳細を格納する配列。メール本文から抽出した内容をこの配列に追加します。
メール本文から積立予定日を抽出
まずは積立予定日を抽出します。
カレンダーに登録する際に指定する日付に使用します。
lines.forEach(function(line) {
if (line.includes(KEYWORDS.INVESTMENT_DATE)) {
investmentDate = line.replace(KEYWORDS.INVESTMENT_DATE, '')
.replace(/(\\d{4})年(\\d{1,2})月(\\d{1,2})日/, '$1/$2/$3');
}
});
メールの各行 (lines) を順に確認し、積立予定日キーワード (KEYWORDS.INVESTMENT_DATE) を含む行を特定します。
特定した行からキーワードを削除し、日付の形式をYYYY/MM/DDに変換してinvestmentDateに格納します。
なお、日付の変換には正規表現を使用しました。
積立内容セクションの開始位置と終了位置を特定
カレンダーの本文に登録する積立内容セクションを抽出するため、開始位置と終了位置を特定します。
var startIdx = lines.findIndex(line => line.includes(KEYWORDS.STARTING_LINE));
var endIdx = lines.findIndex(line => line.includes(KEYWORDS.ENDING_LINE));
lines配列から、積立内容の詳細が始まる行 (KEYWORDS.STARTING_LINE) と終わる行 (KEYWORDS.ENDING_LINE) のインデックスをそれぞれ取得します。
積立内容セクションが存在する場合、情報を抽出
特定した積立内容セクションの範囲内にある情報を抽出します。
if (startIdx >= 0 && endIdx > startIdx) {
investmentDetails = lines.slice(startIdx + 1, endIdx);
}
startIdxとendIdxが有効な範囲である場合、開始行の次の行から終了行の前までの行を抽出し、investmentDetails配列に格納します。
積立予定日と詳細をログに出力
抽出した積立予定日と積立設定の詳細をログに出力します。
Logger.log('積立予定日: ' + investmentDate);
Logger.log('積立詳細: ' + investmentDetails.join('\n'));
investmentDetails.join(‘\n’)で詳細を改行区切りで結合しています。
積立予定日が存在し、積立内容もある場合のみカレンダーに登録
抽出した情報をカレンダーに登録します。
if (investmentDate && investmentDetails.length > 0) {
積立予定日 (investmentDate) と積立内容の詳細 (investmentDetails) が有効な場合にのみ、次のカレンダー登録処理に進みます。
同じ日付のイベントが存在しないか確認
イベントの重複を避ける処理を加えておきます。
var events = calendar.getEventsForDay(new Date(investmentDate));
var eventTitle = '投信積立予定';
if (events.some(event => event.getTitle() === eventTitle)) {
Logger.log('同じ日付のイベントが既に存在します。');
} else {
カレンダーの指定した日 (investmentDate) に既存のイベントを取得し、タイトルが「投信積立予定」のイベントが既に存在するかをチェックします。
既に同じタイトルのイベントが存在する場合、重複を避けるために新しいイベントは作成しません。
イベントの作成
同じ日付にイベントが存在しない場合、新しいイベントをカレンダーに作成します。
var event = calendar.createAllDayEvent(eventTitle, new Date(investmentDate), {
description: investmentDetails.join('\n')
});
event.setColor(CalendarApp.EventColor.GREEN); // 色を設定
Logger.log('カレンダーにイベントを追加しました。');
createAllDayEvent
:終日イベントを作成します。description
:メールから抽出した積立詳細をイベントの説明として追加します。setColor
:イベントの色を設定します。この場合は緑色 (GREEN
) です。
結果をログに出力
結果をログに出力しておきます。
Logger.log('カレンダーにイベントを追加しました。');
イベントの作成が成功した場合、ログに「カレンダーにイベントを追加しました。」というメッセージを出力します。
トリガーを設定
ここまでで作成したイベントを発火させるトリガーを設定します。

過去の受信状況を見た感じでは、投信の次回積立予定日のお知らせは、
- 毎月16日(土日祝日関係なし)
- 11:30前後
に来ているようなので、トリガーは毎月16日の12時~1時で設定しておきます。
実行結果
コードを書きながらテストで動かしている感じだと、問題なく動いているようでした。
※見せない方が良さそうな部分は加工して塗りつぶしています。

===== 2024/10/16 追記=====
予想通り来月分のお知らせが11:59に届きました。

その後、設定した12時~13時の間にトリガーが発火しましたね。

問題なく登録されたようです。よかった。

まとめ

GASを使って楽天証券から送られてくる投信積立予定日をGoogleカレンダーに登録するスクリプトを作ってみました。
慣れない間は理解が難しかったですが、やっていく内にコツが掴めてきました。
今回作成したスクリプトをベースにして、他のメールにも色々応用できそうだなと思いました。
細かい部分の調整や作り込みとかエラー処理の追加とかはまた時間がある時に進めて行こうかなと。
楽天証券で積み立てているものは他にもあるので、その辺もどんどん登録してみたいと思います。


コメント