GAS(Google Apps Script)で下書きメールを作成する

パソコン関連

こちらの回の続きです。

Outlook で送信前チェック
私はもう2011年からフリーランスで仕事をしています。 最近ものすごく驚いたんですが、2011年から仕事をいただいている会社(私の前職です)から、 納品のときのメールにはこのアドレスを含めてください と言われたんです。そのアドレスは前から知...

簡単に言うと、
メールを送信する直前に、

  • 必要なアドレスが入っているか
  • 添付ファイルが漏れていないか

などをチェックしたかったけど、
Gmail だとそういう機能がなさそうだったので
メーラーを Outlook に変更し VBA で運用していたよ。
でも、Outlook で Gmail がめちゃくちゃ使いづらくてどうしよう。

という内容でした。
で、いろいろ考えた結果、Gmail に戻したんです。

そもそもの発想の逆転をしました。
メールを送信する直前でチェックするのではなく、
必要なアドレスや添付ファイルが付いた状態の
メールを作ることができればいいのではないか、と。
GAS の解説をしているサイトとかを見ると、
メールを自動で送信するコードとかがあったんですけど、
自動で送られるの困るなぁ…と思っていました。
が、下書きメールを作成することができると知って、
これだ! と。

で、初 GAS ですがなんとか作れました。
今とりあえずこれで運用できています。
様々な GAS の解説サイトを参考にさせていただきました。
本当にありがとうございました。
私も、誰かの役に立てばいいなぁと思って掲載します。

function CreateDraftMessage() {

  var ssid = " ※案件を管理しているスプレッドシートの SSID ";
  var ss = SpreadsheetApp.openById(ssid);
  var datass = ss.getSheetByName("データ");
  var pdfss =  ss.getSheetByName("請求書");
  var pdfsheetid = " ※PDF印刷用シートの ID ";
  var folder = DriveApp.getFolderById(' ※Google ドライブのフォルダの ID ');

  var sakuzyo_add = ["1@xxxx.com","2@xxxx.com"];
  var youbi = ["日","月","火","水","木","金","土",]

  var lastrow = datass.getLastRow();
  for (let i = 2; i<= lastrow; i++){
    if (datass.getRange(i, 1).getValue() == true){
      var caseId = datass.getRange(i, 16).getValue();
      var url = "https://docs.google.com/spreadsheets/d/" 
        + ssid
        + "/export?gid="
        + pdfsheetid;

      let pdfOptions = "&exportFormat=pdf&format=pdf"
        + "&size=A4" 
        + "&portrait=true"  
        + "&fitw=true"  
        + "&top_margin=0.5" 
        + "&right_margin=0"
        + "&bottom_margin=0" 
        + "&left_margin=0"
        + "&horizontal_alignment=CENTER" 
        + "&vertical_alignment=TOP" 
        + "&printtitle=false" 
        + "&sheetnames=false" 
        + "&gridlines=false" 
        + "&fzr=false" 
        + "&fzc=false" 

      var url = url + pdfOptions;

      let token = ScriptApp.getOAuthToken();
      pdfss.getRange('A1').setValue(i);
      SpreadsheetApp.flush();

      var response = UrlFetchApp.fetch(url , {
        headers: {
          "Authorization": "Bearer " +  token
        }
      });
      var blob = response.getBlob().setName(`さちこ_${caseId}_請求書.pdf`)
      folder.createFile(blob);

      const query = 'label:inbox {subject:【検収】 subject:' + caseId;
      var myThreads = GmailApp.search(query, 0, 1);
      var myMsgs = GmailApp.getMessagesForThreads(myThreads);
      let Body = myMsgs[0][0].getPlainBody().split(/\r\n|\n/);
      let Body_2;

      for (let j=0; j<= Body.length; j++){
        Body_2 = Body_2 + "> " + Body[j] + "\n"; 
      }

      Body_2 = Body_2.replace("undefined","").replace("undefined","");
      var youbi_2 = youbi[Utilities.formatDate(myMsgs[0][myMsgs[0].length-1].getDate(), 'JST', 'u')];

      Body_2 = datass.getRange(i,18).getValue() + "様" + "\n"
        + "\n"
        + "いつもお世話になっております、さちこです。" + "\n"
        + "\n"
        + "検収メールありがとうございました。" + "\n"
        + "請求書PDFを添付します。" + "\n"
        + "\n"
        + "------------------------------------------------------------------------" + "\n"
        + "さちこ" + "\n"
        + "メールアドレス" + "\n"
        + "電話番号" + "\n"
        + "\n"
        + "\n"
        + Utilities.formatDate(myMsgs[0][myMsgs[0].length-1].getDate(), 'JST', 'yyyy年M月d日(' + youbi_2 + ') HH:mm ') + myMsgs[0][myMsgs[0].length-1].getFrom() + ":"
        + "\n"
        + "> \n"
        + Body_2;

      var draft = myMsgs[0][0].createDraftReplyAll(Body_2);

      let address = draft.getMessage().getCc().split(",");
      address.unshift(draft.getMessage().getTo());

      for (let j = 0; j <= address.length-1; j++){
        for (let k = 0;k <= sakuzyo_add.length-1; k++){
          if (address[j].includes(sakuzyo_add[k]) == true ){
            address[j] = "";
          }
        }
      }

      address = address.filter(v => v)
      address.push(sakuzyo_add[0]);
      address.push(sakuzyo_add[1]);

      let options = {
        "attachments":blob,
      };

      draft.update(address.join(), draft.getMessage().getSubject(), draft.getMessage().getBody() ,options);
      datass.getRange(i, 8).setValue(myMsgs[0][0].getDate());
      datass.getRange(i, 9).setValue(new Date());
      datass.getRange(i, 1).setValue("");
      myMsgs[0][0].unstar();
    }
  }  
}

まだ var と let と const の違いもちゃんと理解していないようなコードですが、
一応これで動いています。
必要ない部分を削ったりしたので、
動かないようでしたら教えてください。

各案件はスプレッドシートで管理しています。
スプレッドシートは、案件を管理する【 データ 】というシートと
請求書印刷用で PDF として添付する【 請求書 】というシートがあります。
1案件につき1行で記載していて、
1列目を GAS 実行のキーの列にしています。
16列目が案件ID が入っている列、
18列目がその案件の担当者さんの名前が入っている列、
8列目は検収メールの受信日を入れる列、
9列目はこの検収メールを送信する日を入れる列です。

大まかな流れとしては、

  1. 取引先から『【検収】○○(案件ID)』というタイトルのメールをもらう
  2. スプレッドシートのその案件行の1列目になにか文字を入れて実行
  3. 【 請求書 】シートの A1 に案件IDを入れると
    【 データ 】シートからその案件の情報を参照するので
    その【 請求書 】シートをそのまま PDF にして Google ドライブに保存
  4. 『【検収】』という文字と案件ID をキーにして Gmail を検索、
    1件だけヒットする
  5. そのメールの本文を取得し、受信日時と文の先頭に『> 』(引用符)を追加
  6. 返信メールに書くべきことを記述(「いつもお世話に~」)
  7. 必ず追加しなければいけないアドレス『1@xxxx.com』『2@xxxx.com』が
    すでに含まれている場合は抜いてから加える
    (2通送られないように)
  8. 先ほど作成した PDF を添付し、
    『6』で作った本文と『5』で作った本文を合わせたものを入れて
    下書きメールを作成

という感じです。
一応、複数件を実行できるようになっています。

スプレッドシートでそのまま PDF が作れること、
Google ドライブからファイルを添付できること、が
とっても便利ですね…。
おかげで、検収のメールを受け取ったら、
スプレッドシートの1列目にチェックを入れて実行するだけで
検収メール作成が完了してしまいます。
今まで PDF 作って添付して本文書いて…と、
数分はかかっていたので。今は一瞬です。
一応、添付ファイルや内容を確認しますけどね。

さらに良かったのが、勝手に送信しないところ。
GAS の解説サイトとかを見ると、
「1日100通程度までなら送れる」とか書いてあって、
下書きメールを作成できることに気付かなかったんですね。
内容をチェックしてから自分で送信したかったので、
下書きメールを作成できるのは本当に助かりました。

曜日の取得がよくわからないので、
結構回りくどいやり方になってしまっていますかね…。
まぁ、動くからいいです。

本文を改行をキーにして配列に入れているのは、
文の先頭に引用符をつけるためです。
引用符付けないと返信っぽくないかなーと思って。
で、

Body_2 = Body_2.replace("undefined","").replace("undefined","")

と2回 replace しちゃってるんですが、
『undefined』という文字が1回だと1個残っちゃうんですよね。
調べると、はじめに見つかった文字列しか置換しないとのことで。
2個出てしまうので、2回やっているというわけです。
Excel の SUBSTITUTE とかとは違いますね。
(出ちゃってる原因は対処していない…)

感動したのは、

address = address.filter(v => v)

ですね!
これで配列の空白が詰められるんですね! 便利。
今回は、必ず加えなければいけないアドレスを一旦抜いたときに
空欄になってしまうため、使いました。

address.push(sakuzyo_add[0]);

も便利ですね~。
VBA にもあったりします? 知らないだけかな?

これは検収メールを受信して請求書を付けて返信するコードですが、
このコードを応用して私が納品をするときのメールも作りました。
アドレスが違ったり、PDF の添付がなかったり以外は
ほとんど同じようなコードですが、

let Body = myMsgs[0][0].getPlainBody().split(/\r\n|\n/);

の部分を

let Body = myMsgs[0][myMsgs[0].length-1].getPlainBody().split(/\r\n|\n/);

にしています。
検収のメールはスレッドが1件しかないんですが、
納品の時のメールは何件もやり取りしたあとに送信するんです。
myMsgs の2つ目の添字がそのスレッドの中での各メールになっているようなので、
スレッド内の件数を取得してそこから出しています。
こうすると、一番最後のメールの返信メールを作成することができます。

とりあえず、やりたいと思っていたことはだいたいできたっぽいです。
本当によかった。本当に嬉しい。
本当にありがとうございます。

今までずっと GAS をやってみたいと思っていたんですが、
本を読んでもなかなか入ってこなくて。
切羽詰まらないと、というわけではないんですが、
やっぱり自分で必要だと思ったときがやりどきなんですね。
先日読んだ『なぜ、あなたの仕事は終わらないのか』のとおりですね。

なぜ、あなたの仕事は終わらないのか スピードは最強の武器である
元マイクロソフトの伝説のプログラマーである中嶋聡さんの『なぜ、あなたの仕事は終わらないのか スピードは最強の武器である』を読み終えました。 思わず「ごめんなさい」と謝ってしまいそうになるとても怖いタイトルですが、内容としては中島さんの愛を感...

今のところ、他に GAS の活用法が見つかっていないんですが、
とりあえず心理的な敷居はかなり下がったので、
使えそうなシーンがあったらいろいろチャレンジしてみたいと思いました。
久しぶりに真新しいことは楽しかったです。

再掲になりますが、
これを作るのに様々な GAS の解説サイトを参考にさせていただきました。
本当にありがとうございました!
またよろしくお願いします!

さちこ

40代2児の母。2011年からフリーランスやってます。東京の東の方在住。
第一子が発達グレー男児で、彼が将来彼の妹に迷惑かけずに生きていけるよう、日々奮闘中です。

さちこをフォローする
パソコン関連
さちこをフォローする

コメント

タイトルとURLをコピーしました