GASでGmailを検索する方法〜スレッドフィルターではなくメール単位のフィルターの作り方〜

GAS
この記事は約7分で読めます。

GASでGmailを取り扱う方法の説明、第4回です。第3回を書いた勢いで第4回を書いていきます。

前回は受信したメールの検索に使える検索演算子や正規表現の解説をやってきました。前回の記事は以下をご参照ください。

今回はメールベースでフィルターをかける方法を紹介していきます。

Advertisements

GmailAppのメソッド群で実装されていないメール単位の検索

普通に行けば、GmailAppのGmailThread classやGmailMessage classで実装されているメソッド群を使ってメール単位の検索をすればいいと思うのですが、実は。。。実装されてません。

というわけで、どれだけドキュメントを読み込んでも直接メール単位での検索方法は書かれていません。

あくまで、GmailAppが提供しているのは、第1回でも説明したようなThread単位での検索なのです。

実装されていないのであれば、自分で作るしかない

じゃぁ自分で作りましょう、ということになります。

自分で作るってどうしたらいいの?って思われる方もいるかもしれませんが、実は非常にシンプルで、filterの機能を言葉にしてみるとよくわかります。

Filter: 特定の条件に合致する事項だけ抽出 or 特定の条件に合致しない事項を排除

これって、実はif + matchメソッドの組み合わせとやってることが一緒だと思いませんか?

簡単なメール単位フィルターの実装

何はともあれ実際にどういう形でフィルターが作れるかみていきましょう。まずは前回まで使っていたものにメール単位フィルタを足してみました。

function search_email_and_write4() {
 //A.スプレッドシートの指定と書き出しデータを保持するためのlist作成
  const targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("test");
  let output = []
  
 //①検索する条件を作成
  const start = 0;
  const max = 10;
  const regExp = /Test mail chain/
  const query = "subject: {" + regExp+ "}"
  var threads  = GmailApp.search(query, start, max)


  threads.forEach(function(thread){
    let messages = thread.getMessages();
    messages.forEach(function(message){
      
      let subject = message.getSubject();
      let body = message.getBody();
      let to = message.getTo();
      let cc = message.getCc();
      let from = message.getFrom();
      let attachment = message.getAttachments();
      
      //ここからがメインのメール単位のフィルタリング↓
      //本文に「確認不要」が含まれていないメールのみを対象とする。
      if(body.match("確認不要")){
        return;
      }
      
      //mailadressがtest.comという場合は対象外
      if(cc.match(".*@test.com>$")){
         return;
      }
      
      //toが自分でなければ対象外
      if(!to.match("aaa@bbb.com")){
        return;
      }
      
     
      Logger.log(body);
      let record = [subject, body, from]
      output.push(record)
      
    }
  
                        )
  }
  
  
 )
  let columns = [["subject","body","from"]];
  targetSheet.getRange(1,1,columns.length, columns[0].length).setValues(columns);
  let lastRow = targetSheet.getLastRow();
  targetSheet.getRange(lastRow + 1,1, output.length, output[0].length).setValues(output)
  
  
}

結局メール単位フィルタと言っても何をしているのか?

メール単位でフィルタをしたいとなったときに、結局やれることはgmailappでthreadを抽出してきて、その抽出されたthreadに含まれるmessageを一つづつif条件などでフィルタリングしていくやり方になります。以下の部分がまさにそれです。

threads.forEach(function(thread){
    let messages = thread.getMessages();
    messages.forEach(function(message){

GmailAppのsearchで取得したthreadsをforEachを利用してmessagesとして取得して、さらにそのmessagesをforEachで回して検索して行っています。そうすることで、1つづつのメッセージ(いわゆるメール単位)で条件一致できるかを検証できます。

一つづつのメールが取得できれば、もうあとは、if文でもなんでも一致しないものをreturn;で弾いて行けばいい形です。

//ここからがメインのメール単位のフィルタリング↓
      //本文に「確認不要」が含まれていないメールのみを対象とする。
      if(body.match("確認不要")){
        return;
      }
      
      //ccのmailadressがtest.comという場合は対象外
      if(cc.match(".*@test.com>$")){
         return;
      }
      
      //toが自分でなければ対象外
      if(!to.match("aaa@bbb.com")){
        return;
      }

一致したいものを弾く and 一致していないものを弾く

メール単位で検証する場合、一致しているor一致していないのいづれかの条件を使うことが多いと思います。

上の例示だと、以下のようになります。

一致確認・本文に「確認不要」が含まれていないメールのみを対象とする。
・ccのmailadressがtest.comという場合は対象外
不一致確認・toが自分でなければ対象外

一致・不一致のいづれかの場合に合致したら、return; としてあげることで、そのメールは対象外になり、その後のrecordのpush(アウトプットするlistへの登録)の対象外となり、結果としてフィルタリングできることになります。

まとめ

今回はメール単位でのフィルタリングの仕方の紹介でした。

特定のメールだけを抽出するという機能はGmailAppでは提供されていないので、自作するということになり、具体的な自作方法をみてきました。

正直、複雑なフィルタリング条件だと結構書くのがめんどくさくなります。。また、上記方法だと、コード管理するのも大変になるので、どこかにフィルタリングだけする関数を別に用意して、それを呼び出す方法の方が後々は楽だと思います。

基本実装されていない要素でも考えれば当然できるということを頭に入れてもらいつつ、基本は実装されているものを利用する方が効率的だということも感じてもらえればと思います。

次回がシリーズ最後の予定ですが、messageIDを利用した新規メールの抽出方法です。

第1回:概要・基本的な検索による取得方法・カテゴリ別の取得

第2回:取得したメールをスプレッドシートに書き込む

第3回:検索クエリでよく使う検索演算子と正規表現

第4回(今回):スレッドフィルターではなくメール単位のフィルターの作り方

第5回:messageID(unique ID)を使って新規メールだけシートに追加していく方法

コメント