AppStore,GooglePlayの昨日のレビューをまとめてメール送信

#概要
リリースしているアプリが多いと、レビューのチェックとか大変じゃないですか？

ってことで、前日のレビューをまとめてメールで送ってくるスクリプトをGASで書いてみました！

AppStoreは、レビュー取得のAPIを使いましたが、GooglePlayにはAPIがないのでスクレイピングしてます。
なので、GooglePlayのフォーマットが変わったりすると修正する必要があります。

#レビューメールサンプル
こんな感じで、前日のレビューのみを抽出したメールが毎日送られてきます。

```
■
■ GooglePlay : 激Jパチスロ 2027
■ https://play.google.com/store/apps/details?id=jp.dorasu.apps.and2027
■

2016/3/31 ××××
何もできません ★☆☆☆☆
 落としたけどいきなり停止　何もできません　お金も取られたまんま　安売りの罠


■
■ GooglePlay : 激Jパチスロ BLACK LAGOON
■ https://play.google.com/store/apps/details?id=jp.dorasu.apps.blacklagoon
■

2016/3/31 □□□□
 ★★★★★
 懐かしくてよかった


■
■ AppStore : 激Jパチスロ BLACK LAGOON 2
■ https://itunes.apple.com/jp/app/jijpachisuro-black-lagoon-2/id961851134?mt=8&uo=2
■

2016/03/31 11:24:00 はざず
はあ ★★★★★
おもろい


■
■ AppStore : 激Jパチスロ HIGHSCHOOL OF THE DEAD
■ https://itunes.apple.com/jp/app/jijpachisuro-highschool-dead/id684256528?mt=8&uo=2
■

2016/03/31 08:11:00 らゆなさ
よい ★★★★☆
よいよ、よい

2016/03/31 00:40:00 △△サダオシ
なかなか良い ★★★★☆
実機にかなり近いクオリティー。
```

#レビューをとってくるアプリの指定

アプリIDをB列の2行目から記載したリストを作成します。
スクリプトで使うのはＢ列のみです。

AppStoreとGooglePlayは別のシートで作成し、シート名をスクリプトで指定します。

![シートAppStore-Screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/49562/b511ec8e-8490-63de-1466-351f5fb7ab50.png)

![シートGooglePlay-Screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/49562/a14b1f83-bc9b-bf3d-6550-a30addd46d45.png)


#コード
```js:コード.GS
//
// 昨日のアプリレビューをメール送信する
//
function sendMailYesterdayReview() {
  
  //スプレッドシートに記載のアプリIDのレビューを取得、整形
  var text = "";
  text += jobSheet("GooglePlay", "Dorasu GooglePlay");
  text += jobSheet("AppStore", "Dorasu AppStore");
  
  //取得したレビューをメール送信
  var MAIL_TO    = "XXXX@XXXXX.XXXX,YYYY@YYYYY.YYY";
  var MAIL_TITLE = "アプリレビュー";
  if (text != "") {
    MailApp.sendEmail(MAIL_TO, MAIL_TITLE, text);
    Logger.log("メール送信 to:"+MAIL_TO+"  title:"+MAIL_TITLE+"\n"+text);
  } else {
    Logger.log("昨日分のレビューがないのでメール送信はしませんでした。");
  }
}


// 1シート分の処理
function jobSheet(type, sheetName)
{
  var ROW_START = 2;  //IDが入っている行の開始位置
  var COL_ID    = 2;  //IDが入っている列
  
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  var values = sheet.getDataRange().getValues();

  Logger.log("シート["+sheetName+"] 処理開始");
  
  var cnt=0;
  var text = "";
  for(var i = ROW_START-1; i < values.length; i++){    
    var id = values[i][COL_ID-1];
    text += jobTitle(type, id);
    cnt++;
  } 
  
  Logger.log("シート["+sheetName+"] "+cnt+"タイトル処理終了");
  
  return text;
}

// １タイトル分の処理
function jobTitle(type, id)
{
  var result;
  var text = "";
  
  //レビューをとってくる
  if (type == "AppStore") {
    result = fetchReviewAppStore(id);
    result.type = "AppStore";
  } else {
    result = fetchReviewGooglePlay(id);
    result.type = "GooglePlay";
  }
  
  //エラー
  if (result.error) {
    text = "ERROR : " + result.url + "\n";
    Logger.log(text);
    return text;
  }
  
  //昨日のレビューのみを抽出
  var date = new Date();
  date.setDate(date.getDate()-1);
  result.reviews = extractReviewByDate(result.reviews, date);
    
  if (result.reviews.length > 0) {
    //タイトル
    text += "\n";
    text += "■\n";
    text += "■ " + result.type + " : " + result.title + "\n";
    text += "■ " + result.link + "\n";
    text += "■\n";
    text += "\n";
    
    //レビューを新しい順にソートして追加
    result.reviews.sort((function(a,b){return(new Date(b.date).getTime() - new Date(a.date).getTime());}));
    text += formatReview(result.reviews);
    
  }
  
  Logger.log(id+" "+result.title+" "+result.reviews.length+"レビュー");

  return text;
}


//レビューを整形
function formatReview(reviews)
{
  var text = "";
  
  for (var i in reviews) {
    text = text + reviews[i].date + " " + reviews[i].author + "\n";
    text = text + reviews[i].title + " " + ("★★★★★☆☆☆☆☆").substr(5-reviews[i].rating, 5) + "\n";
    text = text + reviews[i].content + "\n";
    text = text + "\n";
  }
  
  return text;
}

//指定日のレビューのみ抽出
function extractReviewByDate(reviews, date)
{
  var result = [];
  
  date.setHours(0);
  date.setMinutes(0);
  date.setSeconds(0);
  date.setMilliseconds(0);
  
  for (var i in reviews) {
    var d = new Date(reviews[i].date);
    d.setHours(0);
    d.setMinutes(0);
    d.setSeconds(0);
    d.setMilliseconds(0);
    if (d.getTime() == date.getTime()) {
      result.push(reviews[i]);
    }
  }
  
  return result;
}



// AppStoreからレビュー取得
function fetchReviewAppStore(id) {
  var url = "https://itunes.apple.com/jp/rss/customerreviews/id=" + id + "/xml";
  var query = 'select * from xml where url="' + url + '"';
  var response = UrlFetchApp.fetch("https://query.yahooapis.com/v1/public/yql?format=json&q="+encodeURIComponent(query),{'muteHttpExceptions':true});
  try {
    var entrys = JSON.parse(response).query.results.feed.entry;
    var result = {};
    var review = [];
    
    for (var entry in entrys) {
      if (entry == 0) {
        result.title = entrys[entry].name;
        result.link  = entrys[entry].link.href;
      }
      else {
        review.push({
          date:    entrys[entry].updated.replace(/(.*?)-(.*?)-(.*?)T(.*?)-.*/,"$1/$2/$3 $4"),
          title:   entrys[entry].title,
          content: entrys[entry].content[0].content,
          rating:  entrys[entry].rating,
          version: entrys[entry].version,
          author:  entrys[entry].author.name
        });
      }
    }
    result.reviews = review;
    return result;
  } catch (e) {
    return {
      error:   true,
      title:   "",
      link:    url,
      reviews: review
    };
  }    
}


// GooglePlayからレビュー取得
function fetchReviewGooglePlay(id) {
  var url      = "https://play.google.com/store/apps/details?id=" + id;
  var response = UrlFetchApp.fetch(url,{'muteHttpExceptions':true});
  try {
    var result = {};
    var review = [];
    
    result.error = false;
    
    var regexp   = /<div class=\"id-app-title\".+?<\/div>/gi;
    var elements = response.getContentText().match(regexp);
    result.title = elements[0].replace(/.*?>(.*?)<.*/,"$1");
    result.link  = url;
    
    var regexp   = /<div class=\"single-review\".+?class=\"review-link\"/gi;
    var elements = response.getContentText().match(regexp);
    for (var i in elements) {
      review.push({
        author:     elements[i].replace(/.*<span class=\"author-name\">.*?>(.*?)<.*/,"$1"),
        date:       elements[i].replace(/.*<span class=\"review-date\">(.*?)年(.*?)月(.*?)日.*/,"$1/$2/$3"),
        title:      elements[i].replace(/.*<span class=\"review-title\">(.*?)<.*/,"$1"),
        content:    elements[i].replace(/.*<span class=\"review-title\">.*?>(.*?)<.*/,"$1"),
        rating:     elements[i].replace(/.*aria-label=\"5つ星のうち(.*?)つ星で評価しました.*/,"$1")
        
      });
    }
    result.reviews = review;
    
    return result;
  } catch(e) {
    return {
      error:   true,
      title:   "",
      link:    url,
      reviews: review
    };
  }
}
```