やったてみた

Google App Script(GAS)でGmailを取得しSpreadSheetに転機してみた。

GmailをSpreadSheetに自動転記したいという依頼

先日仕事で、「受信したGmailの内容について、速攻で対応したいけど、受信したことに気づかないのでSpreadSheetに自動転記し、転記後にSlack通知とかしたい。」という依頼をいただきました。

スクリプトを使ったGmailの取得や、自動化については経験がなかったのですが、軽く調べたら、Google App Script(以下GAS)でいけそうと思い、軽く即引き受けたらスタックしてしまった経験を記載したいと思います。

SpreadSheetへ自動転記したいGmailの条件

SpreadSheetへ自動転記する条件は下記でした。

  • 件名に「応募」とつくもの
  • 受信したらタイミングでSpreadSheetに転記する

GASを使わずとも、Zapierなど自動化をノーコードで構築できるSaaSがあれば良いのですが、弊社で使用している自動化SaaSは、「Gmailを受信したとき」というトリガーがなく、やはりGASで組むことで進めました。

取得したいGmailの条件を下記で指定できました。受信したタイミングで取得したいのですが、GASで受信したことを検知できないので、時間をトリガーにして、最短の1分毎に受信したメールをチェックする方針で対応しました。

その場合、「該当する件名」と「受信した時間」の2つが一致するものを取得します。

 
// SpreadSheetに書き込んだ時間をgetSheetTimeの変数で取得、unix時間に変換 
const unixTime = Math.floor(getSheetTime.getTime()/1000) 
 // unix時間を文字に変換
const targetTime = unixTime.toString()

//検索条件を配列で格納
const searchCondition = [
      'subject:(応募) ',   // Mailのタイトルに「応募」とあるもの
      `after:${targetTime}`  // 指定の時間より後のもの
    ].join("\u0020"); //半角スペースで連結

// 条件で指定したMailを取得
const myThreads = GmailApp.search(searchCondition, 0, 500)
const myMessages = GmailApp.getMessagesForThreads(myThreads)

取得したい時間について調べてみると、UNIX時間で取れるとあったので、前回取得した時間をSpreadSheet側に残しておき、その時間をUNIX時間に変換して、発動時に条件に入れるようにしました。

ところが、トリガーが発動した1発目はうまくとれましたが、追加で取得していく部分がうまくとれない。。。
いろいろやっているうちに、被ってGmailを取得していることを発見。最初は1分毎のトリガーなので、それでかぶっているかと思いきや、UNIX時間で指定しても時間は無視され、その日のGmailを全て取ってきてしまうことがわかりました。

対処として取った方法は、全て取得をしておいてから、取得したかった日時とGmailの受信日時を比較しフィルターすることにしました。

// SpreadSheetに記載した日付と比較し新しいものだけを残す
 const duplicateMain = setValArray.filter((data) => {
 Date.parse(getSheetTime) <=  Date.parse(data[0])) 
}

取得してからの全体感はこんな感じです。

if(myMessages.length !== 0) {

    // 取得したMailの内容をパースして配列にする
    const setValArray: Object[] = []
        for(let i in myMessages){
          for(let j in myMessages[i]){

            //スターがないメッセージのみ処理
            if(!myMessages[i][j].isStarred()){
             // 受信の日時を取得しフォーマットを変換
              const strDate = myMessages[i][j].getDate();
              // Mailのタイトルを取得。タイトルからどの媒体かを判断
              const strSubject = myMessages[i][j].getSubject(); 
             // MailのBody(本文)を取得
              const strMessage = myMessages[i][j].getPlainBody(); 

             //件名に「Fwd」がなければ追加(転送のメールでない場合)
              if(!strSubject.match(/Fwd/)){
                // 返ってきたオブジェクトを配列に追加
                setValArray.push({start: strDate, subject: strSubject, message: strMessage})
              }

            }// end if

          } // end for j
        } // end for i

      // SpreadSheetの日付よりも新しいものだけ、Fwdの文字列がないものを反映   
     const duplicateMain = setValArray.filter((data) => {
       Date.parse(getSheetTime)  <=  Date.parse(data[0])) 
     }
     // 二次元配列の中に空の配列を持つものがあれば除外する
     const validateSetValArry = duplicateMain.filter((ary) => {
      Object.keys(ary).length) 
    }

  if(validateSetValArry.length){
    // Mailの内容をスプシに反映を書く
    }

 } // end if

これで既に取得しているGmailを除外することはできます。ただ、該当の条件を満たすGmailの受信量が増えると処理時間が増えたりするので、メンテナンスコストなど考慮するとZapierなどの導入がよさそうです。

今回のソースコードはこちらから取得できます。claspでtypescriptにて作成しているので、GASの部分だけが必要の方は、.apps-script内をそれぞれコピーして使ってください。