HeaderLogo
← Works一覧に戻る

Scriptable×GASで家計簿自動化

Scriptable×GASで家計簿自動化

概要

背景として、私の普段使用している家計簿アプリ「Money Tree」ではクレジット決済した時に、即時反映ではなかったのでScriptableというアプリケーションを使用して即時反映させ、さらにiPhoneのウィジットにいくら使ったのかを表示するようにGASと組み合わせて実装しました。

使用技術

ScriptableGASiOSiPhonegitJavaScript

詳細

Scriptable × GASで家計簿を自動化しました。

今回作成したのが画面左上に表示されている今月どのぐらいクレジットカードを使ったのかを即時反映させる仕組みを実装しました。

作ろうと思ったっきっかけ

私は普段、できるだけ全ての買い物などをクレジットカードで行うようにしています。なぜなら家計簿アプリ「Money Tree」とクレジットカードのアプリが繋がっていて、使用した金額を自動で振り分けてくれてお金の流れが把握できるからです。(ポイントが貯まるというのもあります。)

しかし!反映が遅い!

せっかく家計を「見える化」して管理しようとしているのに、数日前の買い物がようやく反映される頃には「これ何に使ったっけ?」となってしまうのはストレスでした。

情報が届くまでのステップ

ステップ①:お店からカード会社への「承認」 レジでカードを通した瞬間に「このカードは有効か?」という確認が行われます。これが「利用可能枠」の減少として即時反映されることはありますが、まだ「確定明細」ではありません。

ステップ②:お店からカード会社への「売上伝票の送付」 これが一番のボトルネックです。お店側がその日の売上データをまとめてカード会社に送る処理をします。毎日送る店もあれば、数日分をまとめて送る店もあります。

ステップ③:カード会社からMoneytreeへの「データ提供」 カード会社にデータが届いた後、さらにMoneytreeがその情報を取得しに行くまでにタイムラグが発生します。

以上のようなステップが存在していて、ここを変えるは不可能です。

そこで、私はクレジットカードを使用した際に飛んでくる「ご利用のお知らせメール」に目をつけました。

今まで使った瞬間に飛んでくるメッセージで「毎回いくら使ったかはわかっているよ・・・」と思っていたものが、そういえばこのメールをうまく使えば反映を待たずとも値段を知ることができるのではないかと考えました。(このメールは即時に飛んでくる)

技術的の選定

そこで色々調べた結果、ScriptableというiOSのアプリケーションが存在していることを知りました。

Scriptable

Scriptableとは一言でいうと、**「JavaScriptを使って、iPhoneやiPadを自分好みにカスタマイズ・自動化できるアプリ」**です。

iOSには公式の「ショートカット」アプリがありますが、Scriptableはそのプロ版・自由形のような存在です。ショートカットでは用意されたアクションを組み合わせますが、Scriptableはコードを書くことで、iOSの内部機能により深くアクセスできます。

通常iOSに対してスクリプトを実行するためにはXcodeを使用して、、署名して、、などiOSのセキュリティや制約を突破するのには骨が折れます。しかしこのアプリはiOS上でJavaScriptを実行できるという最高のアプリだったのです。

GAS(Google App Script)

私はメインのメールとしてGmailを使用しているので、メールを弄るならまずGasで間違い無いと考えました。

データーベースもMySQLなどを考えたのですが、大袈裟なのでスプレッドシートをデーターベース代わりにして、データーをそこに入れていけば気軽にできるはずです。

実装について

まずはGASでメールを取得します。

いつも同じ文面でメールが来るので、それで絞り込むことにしました。

const BASE_SEARCH_QUERY = 'subject:"ご利用のお知らせ" from:statement@vpass.ne.jp';

そして、取ってきたものと内容でクエリを作成して、該当のメールの中身をとってきます。

function fetchPaymentEmails() {
  const searchQuery = buildSearchQueryWithDateRange(BASE_SEARCH_QUERY);
  const threads = GmailApp.search(searchQuery, 0, 50);
  const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_NAME);
  
  // 既存データを一度だけ読み込む(パフォーマンス向上のため)
  const existingData = sheet.getDataRange().getValues();
  
  threads.forEach(thread => {
    const messages = thread.getMessages();
    messages.forEach(message => {
      const body = message.getPlainBody();
      const extracted = parseEmailBody(body);
      if (extracted) {
        const messageDate = message.getDate();
        
        // 重複チェック: 同じタイムスタンプ、金額、利用先の組み合わせが既に存在するか
        if (!isDuplicate(messageDate, extracted.amount, extracted.merchant, existingData)) {
          // 重複していない場合のみ追加
          sheet.appendRow([
            messageDate,
            extracted.amount,
            extracted.merchant,
            'Auto Imported'
          ]);
          
          // 既存データにも追加(次回の重複チェック用)
          existingData.push([
            messageDate,
            extracted.amount,
            extracted.merchant,
            'Auto Imported'
          ]);
        }
      }
      // 処理済みとして既読にする
      message.markRead();
    });
  });
}

重複処理なんかも一応しました。

ちなみに本文の数字の部分の抜き出しなんかは正規表現を使って別関数としてまとめました。

function parseEmailBody(body) {
  const amountMatch = body.match(/利用金額[::]\s*([0-9,]+)/);
  const merchantMatch = body.match(/利用先[::]\s*(.+)/);

  if (amountMatch) {
    return {
      amount: parseInt(amountMatch[1].replace(/,/g, ''), 10),
      merchant: merchantMatch ? merchantMatch[1].trim() : '不明な利用先'
    };
  }
  return null;
}

利用金額と利用場所を正規表現で抜き出して、スプレッドシートに書き出して擬似データーベースの完成です。

あとは、この内容を返すAPIを作成してそれをiPhoneでキャッチするだけです。(タイムスタンプとかも書き出して、記載するように後に修正しました。)

Scriptable側の設定について

Scriptable自体はiOSの中で動くので、

以上のようにiPhoneでぽちぽちスクリプトを書く羽目になりますが、これは書きにくいのでパソコンで一度書き起こしてiPhoneにコピペするようにしました。

ここでFetchして情報を受け取るようにします。

Scritableの組み込み関数はパソコンで書くときに使えないので、そこの部分だけは置き換えるようにしました。

paddingやウィジットの大きさ、色なんかもここで決めることができるようなので、私の場合は緑の銀行を使っているため

そのような色に合わせて作成しました。

完成

時間の周期や最終使用場所など細かい情報もおまけとしてAPIに載せておいて、以上のようなデザインのウィジットを作成しました。

学びと反省

Scriptableは非常に強力なツールだなと思いました。発想次第ではIoT化なんてこともできてしまうかもしれません。

特に今回は、即時で使った金額が把握できるので、明日は外食じゃなくて弁当にしよう!なんて判断もできるようになりました。

ただ、確定の金額では無いので、払い戻しなどがあると計算がずれてしまう可能性があります。

(確か-500円みたいな表示になるから大丈夫な気もするが・・・)

タイムスタンプや重複の処理なども意外と難しかったですが、なんとか今実用化できています。