【GAS】直近で使ったクレカの金額をボタンひとつで取得したい！

## 作成しようとした経緯
毎週金曜日になったらクレカアプリを開いて、先週の土曜日からその日まで使った支出を入力して、取り込んで（取り込むシステムは作成済み。気が向いたらあげます。）って作業が億劫すぎて、自動でできるいい方法がないか模索してました。

:::note info
そのため今回はクレカの使用履歴をボタンひとつでスプシに反映させるまでの処理を紹介します。
:::

### 案1
MoneyForwardと連携して、APIかなんかで取得できるか？
→無料だとAPIを使えない模様（調べた限り。。）なためボツ。

### 案2
クレカのサイトからPDFかCSVデータとしてダウンロードする。
→金額が確定してからじゃないとダウンロード出来ない。今回は直近の使用履歴が知りたいためボツ。

### 案3
クレカを使う毎にメールがくるからそれを使う。
→クレカ情報も必要ないし、良いのでは？

と言うことで案3を採用。

## 簡単なシーケンス図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918071/7fb2939a-4141-c13f-9b86-b9385b591d73.png)


## 具体的なやり方
### ①クレカの使用履歴をGmailに届くように設定
これは各クレカによって異なるので、それぞれで設定をお願いします。

### ②Gmailでラベルを作成する
デスクトップの左のバーのラベル右側にある「＋」ボタンを押下して、ラベルを2種類作成する
1. メールが来たばかりの未処理ラベル（仮に「unprocessed」ラベルとします。）
2. スプシに取り込んで処理済みラベル（こちらは「processed」ラベルとします。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918071/0f4cac32-d76c-fc85-b1ba-947ce77fa512.png)


### ③いよいよGASでコードを書いていく

:::note warn
2025/02/01にコード修正をしてますので、そちらも合わせてご覧いただけますと幸いです。
[修正箇所はこちら](#20250201追記)
    :::

```javascript
function write(){
  // 書き込むシートを取得、アクティブシートを取得しているが任意のシートでOK
  let sheet = SpreadsheetApp.getActive().getActiveSheet();

  // ヘッダーを設定
  var headers = ['Date', 'Price', 'Store'];
  sheet.appendRow(headers);

  // 「未処理」ラベルのメールを取得
  let gmail = getGmail();

  // 要素数分appendRowメソッドを呼び出して最終行に書き込む繰り返し処理を実行
  gmail.forEach(info => sheet.appendRow(info));

  // 確認したメールはアーカイブラベルに変更
  changeLabel();
}

function getGmail() {
  // Gmail取得ここから
  // Gmailのラベルからマッチするメールを一覧化
  let query = 'label:unprocessed';
  let threads = GmailApp.search(query);

  let gmailInfo = new Array();  
  
  //一覧化したメールの件数分繰り返し 
  threads.forEach(function(thread) {

    // スレッド内のメール一覧を取得
    let messages = thread.getMessages();
    
    // メールを一つずつ取り出す
    for(message of messages) {
      // メール本文
      let plainBody = message.getPlainBody();

      // 以下のマッチする箇所を取得するので、それぞれメールに合う形に設定してください。
      // ご利用日時
      let date = plainBody.match(/【ご利用日時\(日本時間\)】　(.*)/);

      // ご利用金額
      let price = plainBody.match(/【ご利用金額】　(.*)円/);
      
      // ご利用先
      let store = plainBody.match(/【ご利用先】　(.*)/);

      //各メール情報を組みとした二次元配列として格納
      gmailInfo.push([date[1],price[1],store[1]]);
    }
  });
    return(gmailInfo); 
}

function changeLabel() {
  let query = 'label:unprocessed';
  let threads = GmailApp.search(query);

  // 新しいラベル
  let processed = "processed";
  var newLabel = GmailApp.getUserLabelByName(processed);
  
  // ラベルがなければ作成する
  if (!newLabel) {
    newLabel = GmailApp.createLabel(processed);
  }

  // 新しいラベルを付ける
  for (var i = 0; i < threads.length; i++) {
    newLabel.addToThread(threads[i]);
  }

  // 現在のラベル
  let unprocessed = "unprocessed";
  var nowLabel = GmailApp.getUserLabelByName(unprocessed);

  // 現在のラベルを外す
  for (var i = 0; i < threads.length; i++) {
    nowLabel.removeFromThread(threads[i]);
  }
  return;
}
```

### ④動作確認
1. スプレッドシートに取得ボタンを作成して、writeメソッドと紐付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918071/303352bb-f84a-b118-f150-b6c368662db4.png)
2. クレカで支払いをしたら、メールが来てることを確認。 
3. 「取得」ボタンを押下して、データが取得できていればOKです。

## 最後に
今回はメールからデータを取得する方法でしたので、次回はZaimと連携して、データを取得する方法をご紹介できたらなと思います。
もっとシンプルにできるとか、こうしたらいいのでは？等ございましたら、コメントお願いします！
良ければいいねお願いします！

## 2025/02/01追記

上記のコードだとメール内に検索ワードがなかった場合に対応できないので、以下のように修正しました。
- 修正して箇所
```diff_javascript
function getGmail() {
  // Gmail取得ここから
  // Gmailのラベルからマッチするメールを一覧化
  let query = 'label:unprocessed';
  let threads = GmailApp.search(query);

  let gmailInfo = new Array();  
  
  //一覧化したメールの件数分繰り返し 
  threads.forEach(function(thread) {

    // スレッド内のメール一覧を取得
    let messages = thread.getMessages();
    
    // メールを一つずつ取り出す
    for(message of messages) {
      // メール本文
      let plainBody = message.getPlainBody();

      // 以下のマッチする箇所を取得するので、それぞれメールに合う形に設定してください。
      // ご利用日時
      let date = plainBody.match(/【ご利用日時\(日本時間\)】　(.*)/);

      // ご利用金額
      let price = plainBody.match(/【ご利用金額】　(.*)円/);
      
      // ご利用先
      let store = plainBody.match(/【ご利用先】　(.*)/);

      //各メール情報を組みとした二次元配列として格納
-     gmailInfo.push([date[1],price[1],store[1]]);
+     // 検索ワードがなかった場合はスキップ
+     if (date != null && price != null && store != null) {
+       gmailInfo.push([date[1],price[1],store[1]]);
+     } else {
+       continue;
+     }
    }
  });
  return(gmailInfo); 
}
```

## 参考先
以下のサイトを参考に作成しました。ありがとうございます。

https://qiita.com/kakakaori830/items/90caa4c7e709ef1d75c2

https://tetsuooo.net/gas/512/