浅野直樹の学習日記

この画面は、簡易表示です

2022 / 12月

GASで宛先ごとに添付ファイルを差し込みメール送信する

タイトルそのままのことを実現します。

給与明細を各従業員に添付ファイルで送るために開発しました。

以前はThunderbirdのMail Mergeアドインでやっていました。

それも悪くはないのですが、毎回CSVファイルを書き換えるのが地味に面倒だと感じていました。

しかも今回は普段ウェブで使っているGmailから送るので、それならGASで実装するのが最も簡単だろうと思い立ち、やってみました。

PythonやPHPでやってもよいのですが、Gmailを操作するのがセキュリティの都合などでややこしかった記憶があり、GASを選びました。

それでは具体的な手順に入ります。

 

1.添付ファイルをアップロードし、名簿のスプレッドシートとメール本文のドキュメントのファイルを作成する

どこかに一意な名前(他のフォルダの名前と重複しない名前で、私の例では「GASブログ公開用」)で新しいフォルダを作成し、そのフォルダの直下に「添付ファイル」フォルダを作ってその中に添付ファイルをアップロードするとともに、名簿のスプレッドシートとメール本文のドキュメントのファイルを作成します。

このようなイメージです。

添付ファイルの名前は、人の名前や現在時刻などから機械的に生成できるようにしておくと後が楽です。

 

2.メール本文のドキュメントの準備

メール本文のテンプレートとなるドキュメントを準備します。

ドキュメントの本文だけを取り出すと、以下になります。

{名前} 様

日々の業務、お疲れさまでございます。

{勤務月}月勤務分の給与明細をお送りいたします。

ご不明点などがございましたら、このメールにご返信いただくか、○○○-○○○○-○○○○までお電話ください。

それでは今後ともどうぞよろしくお願いいたします。

浅野直樹

用途に応じて文面をご自分で作成してください。

人ごとに置き換えたり、プログラムで機械的に作成する部分は、「{名前}」や「{勤務月}」のように{}で囲むようにしておいてください。

ドキュメントの名前は「メール本文」にしてあります。後でこの名前をスクリプトで使います。

 

3.名簿のスプレッドシートの準備

GASには大きく分けてスタンドアロン型とコンテナバインド型があります。

今回はスプレッドシートから名前やメールアドレスといった情報を取得して利用するので、スプレッドシートと結びつけたコンテナバインド型にします。

よって、最初にスプレッドシートを準備します。

最低限、名前とメールアドレスを入力します。所属など、人ごとに置き換えたい部分があればC列以下に追加しておきます。

添付ファイル名を名前や現在時刻などから機械的に生成できない場合は、その人に送るべき添付ファイルの名前もC列以下のどこかに入力しておきます。

ファイル名は何でもよいですが、ここでは「名簿」としておきます。

 

4.スクリプトの記述

「拡張機能」→「Apps Script」を選び、スクリプトエディタを立ち上げます。

プロジェクトの名前は何でもよいですが、ここでは「メール送信」にしました。

用途が一つに定まっている小規模なスクリプトなので、関数名はデフォルトのmyFunctionのままにしています。

完成したコードは次の通りです。

function myFunction() {
  //定数の設定
  const SCRIPT_FOLDER_NAME = 'GASブログ公開用';
  const ATTACHMENT_FOLDER_NAME = '添付ファイル';
  const DOCUMENT_NAME = 'メール本文';

  //フォルダを取得する
  const scriptFolder = DriveApp.getFoldersByName(SCRIPT_FOLDER_NAME).next();
  const attachmentFolder = scriptFolder.getFoldersByName(ATTACHMENT_FOLDER_NAME).next();

  //ドキュメントからメール本文のテキスト(テンプレート)を取得する
  const docFile = scriptFolder.getFilesByName(DOCUMENT_NAME).next();
  const docId = docFile.getId();
  const doc = DocumentApp.openById(docId);
  const docText = doc.getBody().getText();

  //今年と今月と先月を取得し、全員で共通するメールの件名を作成する
  const today = new Date();
  const thisYear = today.getFullYear();
  const thisMonth = today.getMonth() + 1;
  const lastMonth = new Date(thisYear, thisMonth-2, 1).getMonth() + 1;
  const subject = lastMonth + '月勤務' + thisMonth + '月支給分給与明細';

  //スプレッドシートからデータを取得し見出し行を削除する
  const sheet = SpreadsheetApp.getActiveSheet();
  const values = sheet.getDataRange().getValues();
  values.shift();

  //上記データの行ごとにメールを送信する
  for (const value of values) {
    //データから名前とメールアドレスを取得する
    const employeeName = value[0]; 
    const employeeAddress = value[1];

    //メール本文のテキスト(テンプレート)を置換してメール本文を作成する
    const body = docText
      .replace('{名前}', employeeName)
      .replace('{勤務月}', lastMonth);

    //添付ファイルを取得する
    const attachmentFileName = thisYear + '' + thisMonth + '支給' + employeeName + '様給与明細.pdf';
    const attachmentFile = attachmentFolder.getFilesByName(attachmentFileName).next();

     //途中経過をログに出力する
    Logger.log(employeeName + 'さん(' + employeeAddress + ')に' + attachmentFileName + 'を添付したメールを送信します。');

    //メールを送信する(下書き保存する)
    GmailApp.sendEmail(employeeAddress, subject, body, {attachments: [attachmentFile]});
    //GmailApp.createDraft(employeeAddress, subject, body, {attachments: [attachmentFile]});
  }
}

スクリプトの初回実行時には権限の確認をされます。「権限を確認」→(アカウントをクリックして選び)→「詳細」→「メール送信(安全ではないページ)に移動」→「許可」の順番でクリックしてきます。

以下では一つずつ解説します。

  //定数の設定
  const SCRIPT_FOLDER_NAME = 'GASブログ公開用';
  const ATTACHMENT_FOLDER_NAME = '添付ファイル';
  const DOCUMENT_NAME = 'メール本文';

スクリプトを入れたフォルダの名前と添付ファイルを入れたフォルダの名前とメール本文のテンプレートを書いたドキュメントの名前を設定します。

  //フォルダを取得する
  const scriptFolder = DriveApp.getFoldersByName(SCRIPT_FOLDER_NAME).next();
  const attachmentFolder = scriptFolder.getFoldersByName(ATTACHMENT_FOLDER_NAME).next();

スクリプトを入れたフォルダを取得し、その直下の階層にある添付ファイルのフォルダを取得します。

  //ドキュメントからメール本文のテキスト(テンプレート)を取得する
  const docFile = scriptFolder.getFilesByName(DOCUMENT_NAME).next();
  const docId = docFile.getId();
  const doc = DocumentApp.openById(docId);
  const docText = doc.getBody().getText();

ドキュメントで用意したメール本文のテキスト(テンプレート)を取得します。

ドキュメントのファイル名から取得しようとすると、このような冗長なコードになります。

名前からの取得だとファイルのコレクションが返されるので、next()で1つのファイルを取得します。

IDやURLからだとこの部分のコードはシンプルになりますが、IDやURLを取得するのに余計な手間がかかりますし、IDやURLだと何のファイルを取得しているのかコードからわからなくなってしまうので、このようにしました。

  //今年と今月と先月を取得し、全員で共通するメールの件名を作成する
  const today = new Date();
  const thisYear = today.getFullYear();
  const thisMonth = today.getMonth() + 1;
  const lastMonth = new Date(thisYear, thisMonth-2, 1).getMonth() + 1;
  const subject = lastMonth + '月勤務' + thisMonth + '月支給分給与明細';

この部分はGASというより純粋なJavaScriptです。

毎月給与明細を送るという設定ですから、今年と今月と先月を取得しています。

今年の部分は後で添付ファイルの名前として使います。

getMonth()は1〜12ではなく0〜11が返されるので、月の処理は間違えやすいです。

  //スプレッドシートからデータを取得し見出し行を削除する
  const sheet = SpreadsheetApp.getActiveSheet();
  const values = sheet.getDataRange().getValues();
  values.shift();

このようにスプレッドシートから入力されているデータを2次元配列で一括取得し、見出し行を削除して、ループさせるための配列を準備するのがスマートです。

    //データから名前とメールアドレスを取得する
    const employeeName = value[0]; 
    const employeeAddress = value[1];

これが最低限のデータ取得です。所属や添付ファイル名などをスプレッドシートのC列以下に入力している場合は、const affiliation = value[2];のように適宜データを取得して変数に格納してください。

    //メール本文のテキスト(テンプレート)を置換してメール本文を作成する
    const body = docText
      .replace('{名前}', employeeName)
      .replace('{勤務月}', lastMonth);

ドキュメントから取得したメール本文のテキスト(テンプレート)の{}で囲んだ部分をその人ごとに置換してメール本文を作成します。

ここも.replace(‘{所属}’, affiliation)などのように、必要に応じて適宜追加してください。

    //添付ファイルを取得する
    const attachmentFileName = thisYear + '' + thisMonth + '支給' + employeeName + '様給与明細.pdf';
    const attachmentFile = attachmentFolder.getFilesByName(attachmentFileName).next();

添付ファイルの名前から添付ファイルを取得しています。

私の名前の付け方ですと、thisYearとthisMonthの間に空文字列を入れているのが少しわかりづらいですね。これがないと数字の足し算になってしまいます。

スプレッドシートの例えばD列に添付ファイル名を用意している場合は、const attachmentFileName = value[3];のように取得してください。

     //途中経過をログに出力する
    Logger.log(employeeName + 'さん(' + employeeAddress + ')に' + attachmentFileName + 'を添付したメールを送信します。');

このようにログの出力をすると途中経過がわかってよいです。もちろん、ログ出力はプログラムの動作に必須のものではありません。

    //メールを送信する(下書き保存する)
    GmailApp.sendEmail(employeeAddress, subject, body, {attachments: [attachmentFile]});
    //GmailApp.createDraft(employeeAddress, subject, body, {attachments: [attachmentFile]});

ここまでの準備がしっかりできていれば、メールを送信するのはこの一行だけです。

いきなりメールを送信するのが不安なら、sendEmailの行をコメントアウトして、その下のcreateDraftの行をコメントインして、下書き保存を試してみると安心できます。

 

5.参考資料など

Gmailを一斉送信するとき個別に添付ファイルを送りたい。GAS(Google Apps Script)|わきた #ICT教育|noteを主に参考にさせていただきました。

以下の本も大いに参照しました。


詳解!Google Apps Script完全入門 : Google Apps & G Suiteの最新プログラミングガイド


作 者: 

出版社: 秀和システム

発売日: 2018年02月07日

最初は少し大変ですが、このように一度GASを仕込んでおくと次回から簡単に添付ファイルを一括送信できるので、便利です。

 




top