浅野直樹の学習日記

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

浅野直樹

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 Script Perfect Guide Book! : Google Apps & G Suiteの最新プログラミングガイド


作 者: 高橋宣成 著

出版社: 秀和システム

発売日: 2018年02月07日

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

 



令和4年司法試験予備試験成績通知(口述)

令和4年司法試験予備試験口述の成績通知を公開します。

口述試験後なるべく早く、以下のリンク先に再現しました。

合計で122点ということは、どちらも61点だったのでしょうか。

 



令和4年司法試験予備試験成績通知(論文)

令和4年司法試験予備試験論文の成績通知を公開します。過去の結果は以下のリンクにあります。

試験科目 順位ランク
憲法 C
行政法 A
民法 F
商法 C
民事訴訟法 D
刑法 D
刑事訴訟法 E
選択科目 B
法律実務基礎科目 B
合計点 256.06
順位 467

再現答案も過去の記事にありますので、ご参考になれば幸いです。

少しずつではありますが、手応えと結果が一致してきました。

刑事系がまだつかめていません。相対評価ですし、他の人たちのほうができているということなのでしょう。



令和4年司法試験予備試験成績通知(短答)

令和4年司法試験予備試験短答の成績通知を公開します。過去の結果は以下のリンクです。

試験科目 得点
憲法 28
行政法 18
民法 16
商法 10
民事訴訟法 26
刑法 26
刑事訴訟法 22
一般教養科目 51
合計点 197
順位 166

一般教養科目のおかげもあり、短答式の結果は安定してきたかもしれません。



LPIC-2に合格しました

LPIC-2に合格しました。

特に202は難しいですね。後述するようにかなり勉強したのにこの点数です。

 

基本的にはLPIC-1に合格しました – 浅野直樹の学習日記で書いたLPIC-1と同じように勉強しました。

 

The LPIC2 Exam Prepを読み、Ping-tをやり、あずき本、白本、黒本を読み、その他興味に応じて検索したり別の本を読んだりです。

 

最初はまとまった読み物がほしいので、公式筋のAll ResourcesからリンクをたどってThe LPIC2 Exam Prepにたどり着きました。

 

LPIC-1が予想を下回る点数だったので危機感を抱き、今回はPing-tを相当やり込みました。最終的にはコマ問のランダム出題で6〜7割正解できるようになり、WEB問題集のほうはレベル35くらい(模擬試験で基本的に9割以上正解)にまでなりました。

 

あずき本、白本、黒本はそれぞれ2周以上回しました。


LPICレベル2 : Linux技術者認定試験学習書


作 者: 中島能和 著,濱野賢一朗 監修

出版社: 翔泳社

発売日: 2014年05月14日





作 者: 

出版社: 

発売日: 1970年01月01日


LPI問題集Level2〈Ver4.0〉対応 : 試験番号LPI Level2 Exam 201 LPI Level2 Exam 202


作 者: 中島能和 著,ソキウス・ジャパン 編

出版社: インプレスジャパン

発売日: 2014年04月16日





作 者: 

出版社: 

発売日: 1970年01月01日

例によってケチって一つ前のバージョンです。黒本だけ差分も手に入れました。

理解を深めるためにはこれらの本を何周も読み込むに限ります。

 

自分で使ったことがある項目、自分の環境で試してみた項目と、そうでない項目と、それぞれあります。

個人的な経験として、GRUBやファイルシステムなどはlinux環境を構築する際に苦闘してきましたし、HTTP、SSH、FTPあたりは実務的になじんでいました。

リソース監視やバックアップ、ネットワーク関係は自分の環境で試しやすかったです。メールサービスも一度だけ構築したことがあるのでイメージしやすかったです。Sambaも自前の環境で一度は実験しましたが、苦手意識は拭えませんでした。

カーネルやDNSは実験もしづらく難しく感じました。

 

もちろん、わからない用語や違いが気になる対概念などがあれば、その都度検索して解決しました。

 

202は難しいという評判ですし、苦手分野も多かったので、以下の本で別途補強しました。


図解でわかるLinuxサーバ構築・設定のすべて


作 者: 一戸英男 著

出版社: 日本実業出版社

発売日: 2005年05月19日


徹底攻略LPI問題集 : level 3「301/302」対応


作 者: 中島能和 著,ソキウス・ジャパン 編

出版社: インプレスジャパン

発売日: 2008年05月09日


Apacheクックブック : Webサーバ管理者のためのレシピ集 : Apache 2.0,2.2対応


作 者: Ken Coar, Rich Bowen 著,笹井崇司 訳

出版社: オライリー・ジャパン

発売日: 2008年12月11日

若干オーバースペック気味ではありますが、特に『図解でわかる Linuxサーバ構築・設定のすべて』は202の試験範囲とかなりかぶっていますので、オススメです。

 

試験時間はおそらく余るはずですから、慎重に慎重を重ねて見直すことが重要です。今回は油断をせずそれを実践しました。

 

さらにlinuxと仲良くなれたのはよかったです。

 




top