Google App Script 自動化

【GAS】Google Play注文リストの自動生成

はじめに

今回は、GooglePlayの注文リストを自動生成できるようにします。

楽天市場、Amazonの注文リストを自動作成する仕組みまで作りましたので、これが最後となります。

基本的には、受信した注文確認メールの解析をGooglePlay用に入れ替えるだけです。

今回の記事には、過去に受信した注文確認メールからこれまでの購入品リストを作成する方法についても説明しました。

この内容については、楽天市場やAmazonで購入したリストを作成する処理にも適用できますので参照ください。

対象読者

ここで紹介する内容は、以下の条件を満たす方が対象となります。

対象読者

  • Google Playで注文することがある
  • Google Playの注文内容確認メールをGmailで受け取っている
  • Google Playの注文内容をスプレッドシートに自動追加したい

Gmailの内容をGoogle Apps Scriptで解析して、Google Driveに購入品一覧をスプレッドシートに自動追記することを考えます。

また、以下の6つの記事を確認しておくと、理解が早いと思います。

ご存じの方は、読み飛ばしてください。

Google Apps Scriptを使って自動化しよう

GmailにGoogle Apps Scriptからアクセスする方法

Google Apps Scriptで繰り返し実行させる方法

ライブラリ化について

GmailとGoogleカレンダーにアクセスするライブラリについて説明しています。

ここで作成したライブラリ(libCtrlMail、libCtrlCalendar)をの記事でも使います。

注文リストファイル項目追加ライブラリ

Googleスプレッドシートに注文内容を追加するライブラリについて説明しています。

ここで作成したライブラリ(libAddOrderToList)を楽天市場、Amazon、GooglePlayの注文リスト作成に使用します。

注文リストの自動生成

楽天市場の注文リストの自動生成については以下の記事を参照ください。

Amazonの注文リストの自動生成については以下の記事を参照ください。

GooglePlayの注文内容をスプレッドシートに自動追記

Gmailから該当メールを抽出する部分、処理済みメールにラベルを付ける部分は、libCtrlMaillibCtrlCalendarで作った関数を使用します。

主な違いは以下の2点です。

  • Gmailでメールを抽出する際の検索文字が、GooglePlayからの注文確認メール
  • メール本文から注文内容を抽出方法がGooglePlayからの注文確認メールに特化している
  • メールから抽出した内容をスプレッドシートの最終行に追記する

処理概要

GooglePlayを利用すると以下のような利用明細をメールで受け取ることができます。

英語で来る場合と日本語でメールが来る場合があるようなので両方に対応します。

購入情報は、以下のようにスプレッドシートの最下行に自動追加します。

スプレッドシートに利用内容を登録する処理として考えることは以下の通りとなります。

  • GooglePlayからの注文確認メールで未処理のメールを抽出
  • 注文確認メールから、商品、値段、ショップ名を抽出
  • スプレッドシートの最終行に②で得た情報を追記
  • 注文確認メールに処理済みラベルを付ける

これらの処理を定期的に行うことで購入情報が自動的に作られるようになります。

未処理メールの抽出

楽天市場からの注文確認メールで、未処理のメールの抽出は以下の検索文字列で実現します。

メールの抽出条件の構文は、Gmailの抽出条件に設定できる構文と同じです。

// 処理済み後に付けるラベル名(ラベルが存在しなければ自動的に作られる)
var LABEL = '自動処理済';

// GMail検索文字列(Google Playからの投信積み立て完了メール)※未読でラベルなしが条件
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:googleplay-noreply@google.com subject:{"ご注文" "Your Google Play Order Receipt"} -subject:キャンセル ';

処理済みを識別するために、処理完了後に該当メールのラベルに「自動処理済」を付けます。

そのため、抽出条件は以下の4つとなります。

  • 未読であること(is:unread)
  • 「自動処理済」のラベルが付いていないこと(-label:自動処理済)
  • GooglePlayのメールであること(from:googleplay-noreply@google.com)
  • GooglePlayの注文確認メールであること(subject:{"ご注文" "Your Google Play Order Receipt"})
  • キャンセルメールは含めません(-subject:キャンセル)

注文内容抽出

注文確認メールをシンプルテキストで取り出すと以下のようになります。(※一部抜粋)

ここから、商品、値段、支払額、ショップ名、獲得ポイントを抽出します。

下記サンプルメールの赤字部分が抽出対象です。

Google Playの注文確認メールは、日本語と英語のいずれかで来るので両方に対応します。

日本語版注文確認メール

ありがとうございました

Google Play で Google Ireland Limited からの定期購入が完了しました。 定期購
入は、2022/11/08までに解約しない限り自動的に更新されます。定期購入の解約はい
つでも行えます。 定期購入を管理

注文番号: SOP.0000-0000-0000-00000
注文日: 2021/11/08 6:24:28 JST
お客様のアカウント: sekiya-san

アイテム 価格

100 GB (Google One) (開発元: Google LLC) ¥2,500/年

定期購入の自動更新

合計: ¥2,500/年
(消費税 ¥0 込み)

お支払い方法:
Mastercard-0000

Play ポイントを獲得しました
+25

定期購入されると、指定されたお支払い方法で、解約されるまで自動的に上記の定期
購入料金の請求が発生します。解約方法の詳細。お忘れになりませんようお気をつけ
ください。

ご不明な点がありましたら、Google Ireland Limited にお問い合わせください。

Google Play

英語版注文確認メール

Thank you

You've made a purchase from KITXOO on Google Play.

Order number: GPA. 0000-0000-0000-00000
Order date: Nov 8, 2021 4:27:58 PM GMT+9

Item Price

Tasker ¥380

Total: ¥380

(Includes VAT of ¥0)

Payment method:

Mastercard-6500

Play Points earned
+4

Questions? Visit KITXOO.

Google Play

All your entertainment in one place, available anywhere. Learn more ›

See your Google Play Order History.
View the Google Play Refund Policy and the Terms of Service.

Need help? Visit the Google Play help center.
To learn more about Google Payments, visit the Google Payments help center.
Please do not reply to this message.
© 2021 Google | All Rights Reserved.
Google Asia Pacific Pte. Limited, 70 Pasir Panjang Road, #03-71, Mapletree
Business City, Singapore 117371

下記コードが値抽出部分となります。

キーワードを検出し、必要な文字列を抽出します。

  for(var k in threads){
    var thread = threads[k]
    var messages = thread.getMessages();
    for (var j in messages){
      var message = messages[j];
      var body = message.getPlainBody();   // HtmlメールからPlain textの本文を取得する
      var startTime = message.getDate();
      //Logger.log("body=" + body);

      var itemName = null;
      var shopName = null;
      var price = null;
      var count = null;
      var payment = null;
      var shipping = null;
      var point_used = null;
      var point_get = null;
      var flgItemName = null;
      var flgPointGet = null;

      // 注文メールの内容確認
      var rows = body.split("\n");
      for (var i in rows) {
        var row = rows[i];

        // 英語版メールの処理
        if(row.match(/^Item Price/)){
          row = row.replace('\r', '');
          flgItemName = 1;
        }
        else if(row.match(/Total:/)){
          row = row.replace('Total:', '').replace('/month', '').replace('¥', '');
          payment = row;
          flgItemName = 0;
        }
        else if(row.match(/^Play Points earned/)){
          flgPointGet = 1;
        }
        else if(row.match(/^Questions\? Visit /)){
          shopName = row.replace('Questions? Visit ', '').replace('.', '');
        }

        // 日本語版メールの処理
        else if(row.match(/^アイテム 価格/)){
          row = row.replace('\r', '');
          flgItemName = 1;
        }
        else if(row.match(/合計: /)){
          row = row.replace('合計: ', '').replace('/月', '').replace('/年', '').replace('¥', '');
          payment = row;
          flgItemName = 0;
        }
        else if(row.match(/Play ポイントを獲得しました/)){
          flgPointGet = 1;
        }
        else if(row.match(/^ご不明な点がありましたら、/)){
          shopName = row.replace('ご不明な点がありましたら、', '').replace('にお問い合わせください。', '').replace('ヘルプセンター', '');
        }

        if(flgItemName){
          if(itemName == null){
            itemName = '';
          }
          else{
            row = row.replace('\r', '');
            if(row.length > 0){
              itemName += ' ' + row;  
            }         
          }
        }

        if(flgPointGet){
          if(point_get == null){
            point_get = 0;
          }
          else{
            point_get = row.replace('+', '');
            flgPointGet = 0;
          }
        }
      }

      // スプレッドシートに登録
      Logger.log('itemName=' + itemName + ' Total=' + payment + ' point_get=' + point_get + ' shopName=' + shopName);
      libAddOrderToList.registOderInfoToSpreadSheet(
                'test', '', startTime, 'GooglePlay', itemName, price, 1, shopName);
      libAddOrderToList.registOderInfoToSpreadSheetSub(
                'test', '', 0, 0, 0, payment, point_get);

      // スレッドの最初のメッセージを処理したら次のスレッドを処理する
      break;
    }
  }

スプレッドシート登録

スプレッドシートへの登録は、libAddOrderToListライブラリのregistOrderInfoToSpreadSheet()関数と、registOrderInfoToSpreadSeetSub()関数を呼び出して行います。

抽出した、商品、値段、支払額、獲得ポイントを登録します。

      // スプレッドシートに登録
      libAddOrderToList.registOderInfoToSpreadSheet(
                'test', '', startTime, 'GooglePlay', itemName, price, 1, shopName);
      libAddOrderToList.registOderInfoToSpreadSheetSub(
                'test', '', 0, 0, 0, payment, point_get);

スプレッドシート名は「test」となります。※お好みで変更してください。

libAddOrderToListについては以下の記事を参照ください。

全てのコード

/*
 * GooglePlayの注文内容をリストアップする
 * 
 * 2021/11/01 Created by N.Sekiya
 */

// 処理済み後に付けるラベル名(ラベルが存在しなければ自動的に作られる)
var LABEL = '自動処理済';

// GMail検索文字列(Google Playからの投信積み立て完了メール)※未読でラベルなしが条件
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:googleplay-noreply@google.com subject:{"ご注文" "Your Google Play Order Receipt"} -subject:キャンセル ';
//var SEARCH_QUERY = 'from:googleplay-noreply@google.com subject:{"ご注文" "Your Google Play Order Receipt"} -subject:キャンセル';

function googlePlayAddOrderList() {

  var threads = GmailApp.search(SEARCH_QUERY, 0, 1);
  if (threads.length === 0) {
    Logger.log('メールが見つかりません');
    return;
  }

  for(var k in threads){
    var thread = threads[k]
    var messages = thread.getMessages();
    for (var j in messages){
      var message = messages[j];
      var body = message.getPlainBody();   // HtmlメールからPlain textの本文を取得する
      var startTime = message.getDate();
      //Logger.log("body=" + body);

      var itemName = null;
      var shopName = null;
      var price = null;
      var count = null;
      var payment = null;
      var shipping = null;
      var point_used = null;
      var point_get = null;
      var flgItemName = null;
      var flgPointGet = null;

      // 注文メールの内容確認
      var rows = body.split("\n");
      for (var i in rows) {
        var row = rows[i];

        // 英語版メールの処理
        if(row.match(/^Item Price/)){
          row = row.replace('\r', '');
          flgItemName = 1;
        }
        else if(row.match(/Total:/)){
          row = row.replace('Total:', '').replace('/month', '').replace('¥', '');
          payment = row;
          flgItemName = 0;
        }
        else if(row.match(/^Play Points earned/)){
          flgPointGet = 1;
        }
        else if(row.match(/^Questions\? Visit /)){
          shopName = row.replace('Questions? Visit ', '').replace('.', '');
        }

        // 日本語版メールの処理
        else if(row.match(/^アイテム 価格/)){
          row = row.replace('\r', '');
          flgItemName = 1;
        }
        else if(row.match(/合計: /)){
          row = row.replace('合計: ', '').replace('/月', '').replace('/年', '').replace('¥', '');
          payment = row;
          flgItemName = 0;
        }
        else if(row.match(/Play ポイントを獲得しました/)){
          flgPointGet = 1;
        }
        else if(row.match(/^ご不明な点がありましたら、/)){
          shopName = row.replace('ご不明な点がありましたら、', '').replace('にお問い合わせください。', '').replace('ヘルプセンター', '');
        }

        if(flgItemName){
          if(itemName == null){
            itemName = '';
          }
          else{
            row = row.replace('\r', '');
            if(row.length > 0){
              itemName += ' ' + row;  
            }         
          }
        }

        if(flgPointGet){
          if(point_get == null){
            point_get = 0;
          }
          else{
            point_get = row.replace('+', '');
            flgPointGet = 0;
          }
        }
      }

      Logger.log('itemName=' + itemName + ' Total=' + payment + ' point_get=' + point_get + ' shopName=' + shopName);
      libAddOrderToList.registOderInfoToSpreadSheet(
                'test', '', startTime, 'GooglePlay', itemName, price, 1, shopName);
      libAddOrderToList.registOderInfoToSpreadSheetSub(
                'test', '', 0, 0, 0, payment, point_get);

      /*  Googleカレンダーに登録
      libCtrlCalendar.registCalendar(
        calendar, 'GooglePlay注文', 
        startTime, endTime, contents, CalendarApp.EventColor.GREEN);
      libCtrlMail.putLabel(thread);
      */

      // スレッドの最初のメッセージを処理したら次のスレッドを処理する
      break;
    }
  }
}

使用しているライブラリは以下点です。

  • メール処理:libCtrlMail
  • スプレッドシート処理:libAddOrderToList

Googleカレンダーの登録処理をコメントアウトしていますが、有効にすることでカレンダー登録と注文一覧生成を同時に処理できるようになります。この場合、libCtrlCalendarも必要になります。

繰返し実行

正しく動作することを確認したら、繰り返し実行できるよう設定します。

トリガーを設定し、googlePlayAddOrderList()を定期的に呼び出すようにします。

トリガーの設定方法については、以下の記事を参照ください。

繰り返し実行する間隔は、急ぐ必要のない処理なので5分おきにします。

過去の注文履歴を抽出したい場合

これまでにGooglePlayで注文した内容をリストアップしたい場合、Gmailに残っている過去の注文確認メールから作成することができます。

この場合の変更点は2点です。

  • Gmailのメール検索文字から、「未読」「自動処理済ラベル」の指定を外します。
  • メールの検索条件から、gmail.search()の最後の引数を「1」から例えば「100」に変更します。

具体的には以下の通りです。

Gmail検索文字列

var SEARCH_QUERY = 'from:googleplay-noreply@google.com subject:{"ご注文" "Your Google Play Order Receipt"} -subject:キャンセル';

Gmail検索条件

var threads = GmailApp.search(SEARCH_QUERY, 0, 100);

これで、過去にGooglePlayから受信したメールをもとに注文リストを作成することができます。

最後に

いかがでしたでしょうか。

ひとまず注文リストの自動生成はGooglePlayの処理で終わりにします。

他のネットショップの対応が必要だったり、要望があったら考えようと思います。

楽天市場、Amazon、GooglePlayの過去10年分の注文リストを作成してみたら、いろいろと面白いことがわかりました。

購入リストを分析すると新たな知見が得られそうな気がしますので、いずれネタとして公開しようと思います。

では、今日も良い一日を。

-Google App Script, 自動化
-, , , , ,