知識のアウトプットをするブログ

なんでもブログに書き記す

【ライフログ】Kindleの購入履歴を管理する

f:id:seisyo58:20181218131637j:plain
私は、ライフログGoogleカレンダーに集めるようにしている。そこにKindleで買った本も登録している。

なぜGoogleカレンダーに?

カレンダー形式で、いつなのかわかりやすいし、検索もできるから。
以前はEvernoteに1日1ページでライフログノートを作っていたが、Kindleというキーワードで検索したときに、それが含まれるノートは表示されるものの、ノートの中身までは、表示されない。だから、Kindleで買った本を一覧で表示したいなどのニーズには応えられなかった。そういった経緯もあり、今はGoogleカレンダーライフログとして残している。

実際どんな感じなのか

f:id:seisyo58:20181218093900p:plain:w600
現在のライフログカレンダー
現在、Googleカレンダーに集めているログは、

である。

f:id:seisyo58:20181218094158p:plain:w600
Kindleだけ表示するとこんな感じ
f:id:seisyo58:20181218100036p:plain:w600
購入メールとアマゾンに飛べるリンクもある

どうやって実現しているか

f:id:seisyo58:20181218095648p:plain:w600
登録の流れ
Kindleライフログとして残していく上で、私がすることは、Kindleで本を買うことだけだ。すると、自動的にライフログカレンダーに登録してくれる。図でも示しているが、流れとしては

  1. アマゾンから、購入確認のメールが届く
  2. 毎日1回、Google Apps Script(以下GASと表現)で、購入メールを探す
  3. 購入メールが昨日のメールだったら、カレンダーに登録する

となっている。

GAS

まずは全貌

これらを実現しているコードは、以下に示す。

function kindle() {
  var query = 'newer_than:3d from:digital-no-reply@amazon.co.jp ご購入ありがとうございます。 Kindle';
  var threads = GmailApp.search(query,0,50);
  var messages = GmailApp.getMessagesForThreads(threads);
  for(var i=messages.length-1; i>=0 ; i--) {
    for(var j=messages[i].length-1;j>=0; j--) {
      if(yesterday_decision(messages[i][j].getDate())){
        var body = messages[i][j].getBody();
        body = body.replace(/\r?\n/g,"");
        var title = body.match(/title=".*?"/g)[1];
        if(title !== undefined)
          title = title.match(/title="(.*?)"/)[1];
        
        makeCalender(messages[i][j].getDate(),title+"を購入","購入メール\n"+threads[i].getPermalink()+"\n\n買った本\nhttps://www.amazon.co.jp/hz/mycd/myx#/home/content/booksAll/dateDsc/",10);
      }
    }
  }
}

function yesterday_decision(date1) {
  var year1 = date1.getFullYear();
  var month1 = date1.getMonth() + 1;
  var day1 = date1.getDate();
  
  var date2 = new Date();
  date2.setDate(date2.getDate()-1);
  var year2 = date2.getFullYear();
  var month2= date2.getMonth() + 1;
  var day2 = date2.getDate();
  
  if (year1 == year2){
    if (month1 == month2){
      return day1 == day2;
    }
  }
  return false;
}

function makeCalender(date_str,title,text,color){
  if(date_str==="yesterday"){
    var date2 = new Date();
    date2.setDate(date2.getDate()-1);
    date_str=date2;
  }
  var calender = CalendarApp.getCalendarById('abcde@group.calendar.google.com');
  var event = calender.createAllDayEvent(title,new Date(date_str),{description: text});
  event.setColor(color);
  event.removeAllReminders();
}

それぞれの関数ごとに解説していく。

function kindle

これが、1日1回、自動的に実行される関数だ。

//ここで、まず、検索する条件を設定する。
var query = 'newer_than:3d lfrom:digital-no-reply@amazon.co.jp ご購入ありがとうございます。 Kindle';
//該当するスレッドを取得する
var threads = GmailApp.search(query,0,50);
//スレッドからメールを取得する
var messages = GmailApp.getMessagesForThreads(threads);

検索の条件は、"3日以内に、[digital-no-reply@amazon.co.jp]から届いた、「ご購入ありがとうございます。」と「Kindle」というキーワードを含むメール"である。ここの条件は、Gmailの検索と同等のことができるので、調べながら適当に設定してほしい。条件として、3日以内じゃなくて、昨日で検索もできるのだが、そうすると、一部検索から漏れてしまうメールがあったため、過去3日分取得して、そこから昨日かどうか判定するようにした。

スレッドを取得するには、GmailApp.search(query,start,max)を使う。これは、第1引数に検索の条件、第2引数で、何件目から取得するかを、第3引数で、何件取得するかを指定する。ここでは50件としている。まあ、50件もあれば、昨日のアマゾンからのメールはすべて取得できるでしょ、という適当な考えに基づく。


スレッドとメール
スレッドは、複数のメールの集まり。受信したメールに返信したりすると、それらのメールは、一つのスレッドとして、認識される。

次に、それぞれのメールに対する処理をみていく。

for(var i=messages.length-1; i>=0 ; i--) {
  for(var j=messages[i].length-1;j>=0; j--) {
    if(yesterday_decision(messages[i][j].getDate())){
      var body = messages[i][j].getBody();
      body = body.replace(/\r?\n/g,"");
      var title = body.match(/title=".*?"/g)[1];
      if(title !== undefined)
        title = title.match(/title="(.*?)"/)[1];
        
      makeCalender(messages[i][j].getDate(),title+"を購入","購入メール\n"+threads[i].getPermalink()+"\n\n買った本\nhttps://www.amazon.co.jp/hz/mycd/myx#/home/content/booksAll/dateDsc/",10);
    }
  }
}

messagesは二次元配列になっていて、messages[スレッドのindex][メールのindex]となっている。forでスレッドを逆から回し、その中でforでメールを逆から回している。
なんで逆に回しているかというと、最新のメールがmessagesの最初にきているから。過去のメールから順番にカレンダーに登録したかったため。
messages[i][j].getDate()で、そのメールを受信した時間を取得できる。そこから、昨日受信したメールかを調べている。yesterday_decision(何らかの日付データ)という関数を呼び出すと、日付が昨日だったらTrue、違ったらFalseが帰ってくる。
日付が昨日かどうか判定するには、以下のページを参照してみてほしい。
seisyo58.hatenablog.com

getBodyでメールの本文が取得できるので、そこから本のタイトルを取得している。

makeCalender関数で、カレンダーに登録している。

function makeCalender
function makeCalender(date_str,title,text,color){
  //登録するカレンダーをIDで指定
  var calender = CalendarApp.getCalendarById('abcde@group.calendar.google.com');
  //新しいイベントを登録
  var event = calender.createAllDayEvent(title,new Date(date_str),{description: text});
  //イベントの色を指定
  event.setColor(color);
  //イベントの通知をすべて消す
  event.removeAllReminders();
}

解説はコメントを見てほしい。
イベントの色は、このページを参考にしてほしい。
Enum EventColor  |  Apps Script  |  Google Developers

ライフログカレンダーには、過去のデータしかないので、通知は必要ないし、表示がごちゃごちゃになると思ったので、削除している。

毎日実行させるために登録

f:id:seisyo58:20181218131024p:plain:w600
毎日実行される
トリガーをこのように設定することで、毎日実行される。毎日実行され、昨日買った本が、カレンダーに登録される。

まとめ

自動的にライフログが蓄積されていくため、3日坊主などにならない。自動化って素晴らしい。