最近ネットスーパーを使い始めました。Amazonと違って生鮮食品も扱っている(Amazonフレッシュ除く)ので、これからの使用頻度が高まりそうです。
ただ、1つ難点があります。それは、指定した日時に在宅必須なこと。もちろん、一般的な宅急便も、日時指定していれば在宅必須なのですが、突然の日時変更などを行いづらく、よりしっかりと時間を確保しなければならないのがネットスーパーです。
そのため、今までは注文確認メールが来るたびに、それをGoogleカレンダーに登録していました。が、やはり面倒なので、自動で登録できるようにしたいなーと考え、それを行えるスクリプトを作成しました。ここでは、そのスクリプトを紹介します。
SEIYUのネットスーパーを対象
今回紹介するスクリプトは、SEIYUのネットスーパーを対象にしています。
少しコードを変えれば他のネットスーパーでも使用可
もちろん、少しコードを変えれば、他のネットスーパーでも利用できるようになりますので、解説を参考に、変更を加えてみてください。
届くメールはこんな感じ
メールはこんな感じで届きます。
お届け日時は、メールの下のほうに以下のように記載されています。
スクリプトでこのお届け日時を抽出し、カレンダーに登録していきます。
スクリプトの紹介
では、さっそく、スクリプトを紹介します。以下がスクリプトの全文です。
スクリプト全文はこちら!
function myFunction() { //5分後に実行 //未読メールをすべて取得し、SEIYUからのメールがあればお届け日時をカレンダーに登録 var threads = GmailApp.search('label:inbox is:unread'); for(var n in threads){ threads[n].markRead(); //何度も実行されることを防ぐためにメールを既読にする var message = threads[n].getMessages(); var subject = message[0].getSubject(); //メールの件名が'[ご注文確認]SEIYUドットコム'なら登録 if(subject == '[ご注文確認]SEIYUドットコム'){ var body = message[0].getPlainBody(); //お届け日時の書かれた部分を正規表現で取得 //本文からこんな感じの文書を取得→・お届け日時:2018/07/15 16時00分~18時00分 var reg = /・お届け日時:.*?分~.*?分/; var str = body.match(reg)[0]; //カレンダーに登録 var title = "SEIYUネットスーパー"; var date = fetchData(str,':',' '); var startTime = transTime(fetchData(str,' ','分~'),date); var endTime = transTime(fetchData(str,'~','分'),date); CalendarApp.getDefaultCalendar().createEvent(title, startTime, endTime); } } } function fetchData(str, pre, suf) { //strからpreとsufで囲まれた文章を取得。preとsufは削除 var reg = new RegExp(pre + '.*?' + suf); var data = str.match(reg)[0] .replace(pre, '') .replace(suf, ''); return data; } function transTime(time,date){ //timeをカレンダーで扱える形式に変換する。timeの冒頭にdateを付与 time = time.replace('時',':'); time = date + ' ' + time; time = new Date(time); return time; }
処理のプロセスを確認しよう
では、処理のプロセスを紹介していきます。処理は、
- 5分毎に実行されるスクリプトで、未読メールをすべて取得
- メールの件名が、「[ご注文確認]SEIYUドットコム」の場合に、登録処理を実行する
- メールから本文を取得し、正規表現を利用してお届け日時を取得
- 取得したお届け日時でカレンダーに登録
となります。
ざっとでいいので確認しておきましょう。
解説&使い方
まずはトリガーを設定しよう
まずは、メインで実行されるスクリプト、myFunctionを5分後に実行されるように、トリガーを設定しましょう。スクリプトエディタのメニュー、「編集」→「現在のプロジェクトのトリガー」をクリックしてください。
すると、トリガーを設定する画面が表示されますので、新しいトリガーを追加し、以下のように設定してください。
未読メールをすべて取得しよう
処理の初めに、その時点での未読メールをすべて取得してしまいましょう。以下の部分ですね。
var threads = GmailApp.search('label:inbox is:unread');
GmailApp.search('文字列')では、Gmailで検索した際にヒットするスレッド(メール)をすべて取得します。実際に、'label:inbox is:unread'と検索してみればわかるかと思いますが、すべての未読メールがヒットします。これでヒットしたスレッドを取得するのですね。
もし、スクリプトが実行されるトリガーに、新規メール受信時というのがあれば、そのメールだけ取得し、それがネットスーパーからのものであれば処理を実行、で良いのですが、そのようなトリガーはありません。
そのため、メインの処理であるmyFunctionを5分ごとに実行されるようにし、未読メールをすべて取得という形にしています。
スレッドから件名を取得→ネットスーパーからのメールか判定
次は、取得した未読のスレッドのそれぞれから件名を取得し、ネットスーパーからのメールか判定する処理です。
まずは、ループ処理によって、取得したすべてのスレッドを処理していきます。
for(var n in threads){
また、これらの処理は5分後に行われますので、再度実行された際に、一度処理されたメールが再度処理されないように、一度処理したスレッドは既読にしています。
threads[n].markRead(); //何度も実行されることを防ぐためにメールを既読にする
そして、スレッドからメッセージを取得します。ここでいうメッセージは、メール本文のことではないので注意してください。
var message = threads[n].getMessages();
取得したメッセージから、メールの件名を取り出し、
var subject = message[0].getSubject();
それがSEIYUから送られてくる注文確認メールの件名である、「[ご注文確認]SEIYUドットコム」であるかを判定します。
if(subject == '[ご注文確認]SEIYUドットコム'){
本文から正規表現を用いて抽出
次は、メッセージから本文を抽出し、そこからお届け日時を正規表現で取得する処理についてです。
以下のコードでメッセージから本文を取得できます。
var body = message[0].getPlainBody();
そして、取得した本文から、正規表現を用いてお届け日時を取得します。先ほども少し掲載しましたが、SEIYUから送られてくるメールは、以下のような形式でお届け日時が記載されています。
つまり、本文中から、「・お届け日時:yyyy/mm//dd hh時mm分~hh時mm分」となっている部分を検索し、抽出しなければなりません。それには、正規表現を使用すると便利です。今回の場合、正規表現は以下のようになります。
var reg = /・お届け日時:.*?分~.*?分/;
正規表現は、スラッシュで囲んで記述します。「.*?」と書かれた部分には、何かしらの文字が含まれていても抽出できます。このように記述することで、本文から、お届け日時を抽出できます。
そして、実際に抽出するのは以下のコードです。
var str = body.match(reg)[0];
本文から正規表現にあった文章を抽出し、それをstrに代入しています。
カレンダーに登録しよう
では、いよいよカレンダーに登録していきましょう。カレンダーに登録するためには、そのイベントのタイトルと開始日時、終了日時が必要になります。それをこれから指定していきましょう。
まずはタイトルからです。これは任意で決めてしまって構いません。私は便宜上、「SEIYUネットスーパー」としました。
var title = "SEIYUネットスーパー";
そして、開始日時、終了日時ですが、これには少し工夫が必要になります。形式の統一された、日付オブジェクトで用意する必要があります。
日付だとどんな形式でも良いと思われるかもしれません。たとえば、「2018年7月1日 16時30分」でも、「2018/07/01 16:30」でも、同じに見えますし、人間が見る分にはなんの問題もありません。しかし、コンピュータで日付を処理する際には、決められた形式の日付オブジェクトでなければ今回のスクリプトは動作しません。
なので、日付オブジェクトを用意することになります。それを作成するためには、日付と時間を文字列の形式で用意し、new Date()を用いる必要があります。引数は日時が記載された文字列で構いません。なので、まずは日付を示す文字列を用意しましょう。
その文字列は、「2018/07/01 16:30」の形式で用意するのが良いでしょう。したがって、抽出したお届け日時を、これらの形式に整える必要があります。
まずは、お届け日を抽出しましょう。以下のコードで抽出できます。
var date = fetchData(str,':',' ');
このfetchData()という関数は、実は予め用意されているものではなく、自作の関数です。文字列と、正規表現の開始文字と終了文字を指定することで、文字列から該当の箇所を抽出し、しかも開始文字と終了文字を削除するようにしています。その関数は以下のようになっています。
function fetchData(str, pre, suf) { //strからpreとsufで囲まれた文章を取得。preとsufは削除 var reg = new RegExp(pre + '.*?' + suf); var data = str.match(reg)[0] .replace(pre, '') .replace(suf, ''); return data; }
この関数では、先ほど使用したような正規表現をオブジェクトで用意し、strから抽出、その抽出された文章から、replace()関数を使って、開始文字と終了文字を削除しています。
次に、開始日時と終了日時を取得します。これは、以下のようなコードになります。
var startTime = transTime(fetchData(str,' ','分~'),date); var endTime = transTime(fetchData(str,'~','分'),date);
fetchData()関数を使っているのは先ほどと同じですが、これにはtransTime()という関数も使用しています。これも自作関数で、以下のようになっています。
function transTime(time,date){ //timeをカレンダーで扱える形式に変換する。timeの冒頭にdateを付与 time = time.replace('時',':'); time = date + ' ' + time; time = new Date(time); return time; }
SEIYUのネットスーパーの場合、時間が「16時00分」といった具合に、hh時mm分という形式になっています。これでは日付オブジェクトを作成できず、hh:mm といった形式にしなければなりません。なので、その形式に整形し、日付オブジェクトを作成し、それを返すのがtransTimeです。時間だけでなく、日付も必要なので、それを引数にし、関数内で結合しています。
最後に、これをカレンダーに登録します。以下のコードで登録できます。
CalendarApp.getDefaultCalendar().createEvent(title, startTime, endTime);
注意点
今回紹介したスクリプトでは、未読メールからお届け日時を取得しています。なので、スクリプトが実行される前にメールを読んでしまうと、スクリプトが実行されません。
また、スクリプトが実行されるたびにメールが既読になってしまいます。なので、それが困るという方は、処理されたかどうかを、スプレッドシートなどにスレッドのIDと紐づけて管理すると良いかと思います。
参考など
今回、こちらのスクリプトを作成するにあたって、以下のブログと書籍を参考にしました。
特に、fetchData関数は丸パクリで、正規表現の基本知識も上記ブログで学びました。
よろしければ、参考にしてください。