Last active
October 10, 2024 07:51
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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