Google Apps Script

Google Apps Script プログラミング (4) シートの情報を使ってメールを送る

※この記事は、「Google Apps Script プログラミング (3) Gmail を使ってメールを送る」の続きです。

前回は、Gmail アカウントを使ったメール送信を行いました。今回はこれまで学んできたことを応用して、複数の人に一斉にメールを送るようにします。

今回のゴール

シート上で、送信対象にチェックを入れている人に対して、メールを送信する予定のプログラムを作ります。
※実際には存在しないメールを用いていますので、送信の一歩手前の実行ログ確認までです。ご承知おきください。

ここで、実現している機能は、以下の通りです。

  • シート上のデータから、送信対象にチェックが入っている人の「名前」と「メールアドレス」を取得する
  • 取得したデータを使って、対象者にメールを送信する

(1) 準備をします

1) 元となるデータをダウンロードします

今回の練習をするための、元となるデータを用意します。以下のリンクをクリックして「akoroushi.csv」をダウンロードしてください。

akoroushi.csv

このファイルは、CSV形式のファイルです。CSV とは Comma-Separated Value (カンマで区切られた値)という意味です。ダウンロードした akoroushi.csv を Windows の「メモ帳」で開くと、カンマで区切られた値が入ったデータになっていることが分かります。

akoroushi.csv の中身です

2) CSVファイルを Google スプレッドシートに取り込みます

前回使用した Google スプレッドシートのシートの情報は空欄です。ここに、CSVファイルの内容を取り込みます。[ ファイル ] から [ インポート ] を選びます。

インポートするデータを選択します。手元にダウンロードした akoroushi.csv を入れ込みますので、[ アップロード ] を選択してください。

[ アップロード ] の枠の中に、akoroushi.csv をドラッグして入れます。

このように入れ込んでみてください

インポートの方法を、以下の通り指定します。

  • 場所のインポート
    現在のシートに追加する
  • 区切り文字の種類
    カンマ
  • テキストを数値、日付、数式に変換
    はい
インポートの方法を指定します

ファイルをインポートし終えると、以下のような表示になります。

ファイルのインポート直後の様子

名前・メールアドレス・送信対象が表示されます。
※メールアドレスは架空のもので、存在しません。ご注意ください。

送信対象は TRUE (真) と FALSE(偽) のいずれかになっています。このような真偽いずれかの値を持つデータの型式のことを「ブーリアン型(ブール型・論理型)」と言います。これは、主に「チェックボックス」にチェックの有無と同様に扱われますので、見た目もチェックボックスにしてしまいましょう。

表計算ソフトでは、シート上にある一つひとつのデータ枠のことを「セル」と呼びます。セル C2~C9 の範囲を選択してから、[ 挿入 ] → [ チェックボックス ] を選ぶと、TRUE / FALSE がチェックボックスでの表示に変わります。

(2) チェックが入っている人の情報をまとめます

1) 前記事終了時点のコードを確認します

前記事 Google Apps Script (3) が終わっている時点では、以下のようになっています。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  for(let num = 1; num <= 3; num++){
    let message = "これは" + num + "回目のテストメッセージです";
    sheet.getRange(num,1).setValue(message);
  }
}

function sendEmail() {
  const okurisakiAddress = "milldesign08@gmail.com";
  const honbun = "こんにちは、これはプログラムを使って送信したメールです";
  const kenmei = "連絡:初めてのメール"

  GmailApp.sendEmail(okurisakiAddress, kenmei, honbun);
}

これから、myFunction() の中を書き換えていきましょう。

2) 全データをまとめて取得します

シート上のデータを、まとめて取得します。myFunction() の中身 を、このように書き換えましょう。書き換えるのは3行目から13行目の箇所です。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();
  let akoName = "";
  let akoEmail = "";
  Logger.log(data);

 /* ここから先はコメント扱いにしておきます
  for(let num = 1; num <= 3; num++){
    let message = "これは" + num + "回目のテストメッセージです";
    sheet.getRange(num,1).setValue(message);
  }
  */
}
  • 3行目
    シート上のデータを全てを取り出して、配列 data に入れます。

  • 4行目
    チェックの入っている人の名前を入れる変数 akoName を宣言しています。

  • 5行目
    チェックを入っている人のメールアドレスを入れる変数 akoEmail を宣言しています。

  • 6行目
    配列 data の中身を、実行ログに出力します。

  • 8行目 と 13行目
    /* から始めて */ で終えると、その間に囲まれたものをコメント扱いにして、プログラム実行の際に無視させます。この操作のことを「コメントアウト」と言います。コメントは、後になってプログラムを読み返す際に、自分自身や他者に何を目的で書いているのか、分かりやすくする目的で書きます。ここでは、後になって再利用するものなので、記述を残しているのです。

それでは、「myFunction」を「▷実行」します。
※前記事の最後に実行したのは、「sendEmail」でしたから、実行する関数を「myFunction」に切り替えてから実行しましょう。

実行する関数を myFunction にしてから「実行」

実行すると、以下のような表示になります。

実行ログに配列 data の中身がまとめて出力されます

3) 配列とは

実行ログに、何やら長く情報が載りました。つい先程見た、akoroushi.csv に中身が似ています。これは「配列」というデータ構造の中に、シート上のデータを取り込んだからです。これを少し整理して、下に表示してみます。

実行ログの情報は、データ全体及び 1行分のデータが [ ] で囲まれています。また、列ごとのデータの区切りには , が使われています。これらは配列の中での枠の区切りを示しています。

配列 data の構造を見ると、先程の枠の区切りに基づいて、取り込んだデータを data[0][0] から始まって data[8][2] までの枠に収めているのです。例えば、「大石主税」を取り出す場合は、data[2][0] と示します。

data[2][0] と書くと、取り出されたのは 大石主税

この時に使う[] 内の数値のことを、インデックス(index・添字)と言います。この数字を有効に使って、「全ての行で、『送信対象』のチェックが入っている(TRUEである)人の情報を取り出す」操作をします。

4) for の繰り返しを使って必要な情報をまとめます

それでは、プログラムを以下のように書き換えてみましょう。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();
  let akoName = "";
  let akoEmail = "";
  const lastRow = data.length - 1;

  for(let num = 1; num <= lastRow; num++){
    if(data[num][2]){
      akoName = akoName + data[num][0] + "\n";
      akoEmail = akoEmail + data[num][1] + ",";
    }
  }
  Logger.log(akoName);
  Logger.log(akoEmail);
}
  • 6行目
    データ全件の行数を 変数 lastRow に入れています。data.length とは、配列内の行データの数です。ここでは [0] ~ [8] までの 9行分で「9」と求められますが、実際には[0] は見出し行で、データは [8] までなので、9 – 1 = 8 を、lastRow に入れています。

  • 8行目
    num が 1から、変数 lastRow 以下(8以下)の間、繰り返す宣言をしています。

  • 9行目
    if文です。ifの中に条件式を入れて、条件式が TRUE だった時に { } 内の処理(10行目と11行目)を行います。今回の場合、data[num][2] が TRUE だった場合 (送信対象が TRUEだった場合、つまりチェックが入っている場合)と書いています。

  • 10行目
    9行目が TRUE だった際の、名前情報をまとめる処理です。
    名前は、data[num][0] に入っています。これを取り出して、変数 akoName につなぎ合わせています。最後に書いてある「 \n 」は、「¥の半角文字」+「n」と入力します。これは「エスケープ文字」と呼ばれているもの一つで、「\n」は改行を示します。

  • 11行目
    9行目が TRUE だった際の、メールアドレス情報をまとめる処理です。
    メールアドレスは、data[num][1] に入っています。これを取り出して、変数 akoEmail につなぎ合わせています。

  • 14行目
    変数 akoName の値を、実行ログに出力します

  • 15行目
    変数 akoEmail の値を、実行ログに出力します

実行結果は、以下の通りです。

チェックの入った5名の名前と、メールアドレスが表示されます

チェックの入っていた 5名の名前とメールアドレス情報を、取りまとめることが出来ました。

今度は、これを使って 機能 sendEmail() を動かす準備をしましょう。

(3) まとめた情報を使ってメールを送ります

1) sendEmail() のメール送信機能を止める

まずは、準備のため sendEmail() の一部をコメントにしておきましょう。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();
  let akoName = "";
  let akoEmail = "";
  const lastRow = data.length - 1;

  for(let num = 1; num <= lastRow; num++){
    if(data[num][2]){
      akoName = akoName + data[num][0] + "\n";
      akoEmail = akoEmail + data[num][1] + ",";
    }
  }
  Logger.log(akoName);
  Logger.log(akoEmail);
}

function sendEmail() {
  const okurisakiAddress = "milldesign08@gmail.com";
  const honbun = "こんにちは、これはプログラムを使って送信したメールです";
  const kenmei = "連絡:初めてのメール"

 /* 誤ってメールを送らないように、コメントにします。
  GmailApp.sendEmail(okurisakiAddress, kenmei, honbun);
  */
}

23行目から25行目の内容を、まとめてコメントにしました。存在しないメールアドレスに対してメールを送信するのを防ぐためです。

2) sendEmail() へチェックした情報を渡す

myFunction() で「チェックの入った人の情報を取りまとめた」ので、これを sendEmail() に渡します。myFunction() の一部と、sendEmail() の一部を、以下のように書き換えます。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();
  let akoName = "";
  let akoEmail = "";
  const lastRow = data.length - 1;

  for(let num = 1; num <= lastRow; num++){
    if(data[num][2]){
      akoName = akoName + data[num][0] + "\n";
      akoEmail = akoEmail + data[num][1] + ",";
    }
  }
  sendEmail(akoEmail, akoName);
}

function sendEmail(okurisakiAddress, member) {
  const honbun = "12月14日は、吉良邸への討ち入りです。\n\n" + "■表門担当者\n" + member;
  const kenmei = "連絡:討ち入り日程について"

  Logger.log(honbun);
  Logger.log(okurisakiAddress);

 /* 誤ってメールを送らないように、コメントにします。
  GmailApp.sendEmail(okurisakiAddress, kenmei, honbun);
  */
}
  • 14行目
    myFunction() の中から、sendEmail() を呼び出しています。その際に、sendEmail(akoEmail, akoName) と書くことで、変数 akoEmail と 変数 akoName を渡しています。
    sendEmail() 実行後は、この行に再び戻ってきます。

  • 17行目
    14行目で、sendEmail() を実行するために変数 2つが指示されたので、それに合わせて 機能を拡張します。ただし、この sendEmail() の中では、受け取ったものをそれぞれ okurisakiAddressmember という名前で扱います。
    • okurisakiAddress … 受け取った akoEmail の値を示しています
    • member … 受け取った akoName の値を示しています

  • 18行目
    変数 honbun (メール本文)を決めています。討ち入り日程に関する情報です。チェックの入った人の名前は、■表門担当者 として、メール本文中に記載されます。

  • 19行目
    それっぽい件名にしています。深い意味はありません。

  • 21行目
    変数 honbun の値を、実行ログに出力します

  • 15行目
    変数 okurisakiAddress の値を、実行ログに出力します

それでは、「myFunction」を「▷実行」します。

実行結果で、メール本文と送信先メールアドレスが表示されます

このように、メールを送信する準備までが整いました。GmailApp.sendEmail()を含むコメントを解除すれば、実際にメールが送信されます(しつこいようですが、今回は架空のアドレスですので、送信しません)。

小さなプログラムの積み重ねから

ゴールまでの間に、小さなプログラムを積み重ねていきました。Logger.log () を使いながら、実行ログを一つひとつ確認して進めてきました。

このように、小さなステップで確認しながら作っていくと、間違いに気づきやすいです。何度エラーを出してもよいので、少しずつ作っていきましょう。

(余談:今回は、赤穂浪士の名前を使いました。討ち入りの際に、表門隊・裏門隊の二手に分かれたことになっています。チェックしてみて分かったのですが、親子関係・兄弟関係にある二人が参加していた場合、例外なく分かれて配属されていたのでした。本当は、呪術廻戦のキャラクター名を使おうとしたのですが、内容に疎いので遠慮しました。)