Skip to content

Instantly share code, notes, and snippets.

@umechanhika
Last active October 10, 2024 07:51
Show Gist options
  • Select an option

  • Save umechanhika/91d10ea857f549a06faeaadb19569ac9 to your computer and use it in GitHub Desktop.

Select an option

Save umechanhika/91d10ea857f549a06faeaadb19569ac9 to your computer and use it in GitHub Desktop.
Code to retrieve card usage notification emails from Gmail and log them in a spreadsheet budget tracker.
/**
* Gmailのカード利用通知を、スプレに転記する処理をまとめたgsファイル。
*/
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const PAYMENT_MANAGEMENT_SHEET_NAME = "支出管理シート";
/**
* この処理を時間トリガーで毎分実行する
*/
function recordPayment() {
const paymentMessages = getPaymentMessages();
const records = createRecords(paymentMessages);
addRecords(records);
markReadAndDelete(paymentMessages);
}
/**
* 支払い通知メールの取得
*
* メールはGmailMessage型の配列で返却される。
* SMBCデビットの利用通知メールの内、月内の未読メールのみを抽出する。
*/
function getPaymentMessages() {
// 検索クエリ用の開始日付を取得して、クエリ文字列用にフォーマット変更
const startDate = getStartDate();
console.log(`startDate: ${startDate}`);
const startDateValue = startDate.valueOf() / 1000; // ミリ秒から秒に変換
console.log(`startDateValue: ${startDateValue}`);
// 検索クエリ用の終了日付を取得して、クエリ文字列用にフォーマット変更
const endDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 0, 23, 59, 59); // 時間指定しないと月末最終日のメールがクエリに引っかからない
console.log(`endDate: ${endDate}`);
const endDateValue = endDate.valueOf() / 1000; // ミリ秒から秒に変換
console.log(`endDateValue: ${endDateValue}`);
// 検索クエリ作成
const gmailSearchQuery =
"from: smbc-debit@smbc-card.com " +
"subject: ご利用のお知らせ【三井住友カード】 " +
"is: unread " +
`after: ${startDateValue} ` +
`before: ${endDateValue} `;
console.log(`gmailSearchQuery: ${gmailSearchQuery}`);
// スレッドの取得
const threads = GmailApp.search(gmailSearchQuery);
console.log(`threads count: ${threads.length}`);
// スレッド内のメッセージの取得
const paymentMessages = [];
threads.forEach(thread => {
const messages = thread.getMessages();
console.log(`messages count: ${messages.length}`);
// スレッド内の未読メッセージを配列に追加
messages.forEach(message => {
console.log(`message.isUnread(): ${message.isUnread()}`);
if (message.isUnread()) {
paymentMessages.push(message);
console.log(`message: ${message.getPlainBody()}`);
}
});
});
console.log(`unreadMessages count: ${paymentMessages.length}`);
return paymentMessages;
}
/**
* メール検索のクエリに使う検索開始日を取得する
*
* スプレのタイトルに年月を入れる運用のため、タイトルを取ってきてDate型に変換して返却する。
*/
function getStartDate() {
const spreadsheetName = spreadsheet.getName();
console.log(`spreadsheetName: ${spreadsheetName}`);
const spreadSheetNameDate = Utilities.parseDate(spreadsheetName, 'JST', 'yyyy年M月');
console.log(`spreadSheetNameDate: ${spreadSheetNameDate}`);
return spreadSheetNameDate;
}
/**
* メッセージからスプレに追加するレコードを生成する
*
* レコードはkey, value形式のオブジェクトの配列として返却される。
* {
* "transactionDate": transactionDate,
* "payee": payee,
* "price": price
* }
*
* 取得できるメッセージの例)
* ◇利用日 :2024/09/04 13:07:05
* ◇利用先 :SHIBUSANYOSHINARI
* ◇利用金額:1,600円
*/
function createRecords(messages) {
const records = [];
messages.forEach(message => {
// 利用日の抽出
const transactionDateSentenceRegex = /◇利用日.*/;
const transactionDateSentence = message.getPlainBody().match(transactionDateSentenceRegex)[0];
console.log(`transactionDateSentence": ${transactionDateSentence}`);
const transactionDateRegex = /\d{4}\/\d{2}\/\d{2}/;
const transactionDate = transactionDateSentence.match(transactionDateRegex)[0];
console.log(`transactionDate: ${transactionDate}`);
// 利用先の抽出
const regexPayeeSentence= /◇利用先.*/;
const payeeSentence = message.getPlainBody().match(regexPayeeSentence)[0];
console.log(`payeeSentence": ${payeeSentence}`);
const regexPayee = /[^◇利用先\s:].*/;
const payee = payeeSentence.match(regexPayee)[0];
console.log(`payee: ${payee}`);
// 利用金額の抽出
const regexPriceSentence= /◇利用金額.*/;
const priceSentence = message.getPlainBody().match(regexPriceSentence)[0];
console.log(`priceSentence": ${priceSentence}`);
const regexPrice = /\d{1,3}(,\d{3})*/;
const price = priceSentence.match(regexPrice)[0];
console.log(`price: ${price}`);
const record = {
"transactionDate": transactionDate,
"payee": payee,
"price": price
};
console.log(`record: ${record.transactionDate}, ${record.payee}, ${record.price}`);
records.push(record);
});
return records;
}
/**
* スプレッドシートの支出管理シートに支払い情報を追加する
*/
function addRecords(records) {
const paymentManagementSheet = spreadsheet.getSheetByName(PAYMENT_MANAGEMENT_SHEET_NAME);
records.forEach(record => {
const lastRow = paymentManagementSheet.getLastRow();
const insertRow = lastRow + 1;
console.log(`insertRow: ${insertRow}`);
const insertRange = paymentManagementSheet.getRange(insertRow, 1, 1, 3);
insertRange.setValues([[record.transactionDate, record.payee, record.price]]);
});
}
/**
* 未読メッセージを既読にして削除する
*/
function markReadAndDelete(unreadMessages) {
unreadMessages.forEach(message => {
message.markRead();
message.moveToTrash();
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment