GoogleAppsScript
spreadsheet

年賀メールをGASで一斉送信

前置き

Gmailからの年賀メール一斉配信をGASで出来ないかと打診があったので、作ってみた。
・スプレッドシートで一覧を作成する
・GASで一覧から情報を取得してメール送信するロジックを作成する
・GASのトリガーでタイマー設定する
という流れ。
GASはスプレッドシートと連携しているタイプ(※)で作成。
※ スプレッドシートのメニューの『ツール』 > 『スクリプトエディタ』からGASのエディタを起動する
(単にスプレッドシートのオブジェクトを取得する際にURLを指定する手間を省いただけで、深い意味は無い)

スプレッドシートのイメージ

一覧.png
こんな感じのスプレッドシートを用意。
K列とL列はスクリプトが処理後に勝手に記入する列。

ソースコード

GASでテンプレートとして使うhtmlファイルと、メインのロジックを記入するmain.gsを作る。

mail_body.html
{company}{department}{to_name}

共通のメッセージ
明けましておめでとうございます。
以下略

株式会社◯◯◯
{from_name}

{message}

こいつの{xxxx}の部分をスプレッドシートから吸い上げた情報で置換してメール本文にする。

main.gs
// メール送信のメインロジック
function sendMail() {

  // 念のため誤作動防止チェック
  if(!_checkTime())return;

  // 対象シートを取得
  var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("対象シート名");

  // 処理領域を取得
  var results = targetSheet.getRange("A3:L").getValues();

  var item = {};
  var body = "";

  // レコード毎に処理
  for(var key in results){
    // レコード単位でエラーハンドリング
    try{
      // 初期化
      item = {};
      body = "";

      // ループ終了の目安(タイトルの記入が無かったら終了)
      if(results[key][0] == "")return;

      // From Addressが実行ユーザと異なる場合はスルー
      if(results[key][1] != Session.getActiveUser())continue;

      // 実行済みはスルー(Result Flagで判定)
      if(results[key][10] == "OK" || results[key][10] == "NG")continue;

      // 一覧から吸い上げたレコードの情報を格納
      item ={
        title:results[key][0],
        from:results[key][1],
        from_name:results[key][2],
        to:results[key][3],
        company:(results[key][4] != "" && results[key][4] != undefined)?results[key][4]+"\n":"",
        department:_createDepartment(results[key][4],results[key][5]),
        to_name:_createToName(results[key][7],results[key][8]),
        message:(results[key][9] != "" && results[key][9] != undefined)?"追伸\n"+results[key][9]+"\n":"",
      };

      // テンプレートをitemで置換していく
      body = _include("mail_body")
      .replace("{from_name}",item["from_name"])
      .replace("{company}",item["company"])
      .replace("{department}",item["department"])
      .replace("{to_name}",item["to_name"])
      .replace("{message}",item["message"]);

      // Gmail API用のGAS既存モジュールから送信
      GmailApp.sendEmail(
        item["to"],
        item["title"],
        body
      );

      // Result Flagの書き込み
      var index = 3 + parseInt(key);
      targetSheet.getRange("K"+index+":K"+index).setValue("OK");

      Logger.log(item["to"]+"へ送信完了");
    }catch(e){
      // Result Flagの書き込み
      var index = 3 + parseInt(key);
      targetSheet.getRange("K"+index+":K"+index).setValue("NG");
      targetSheet.getRange("L"+index+":L"+index).setValue(e.message);      
    }
  }
}

// 誤作動対策で2018年1月1日にしか動かないように
var _checkTime = function(){
  var times = new Date();
  var year = times.getFullYear();
  var month = times.getMonth()+1;
  var day = times.getDate();

  return (year == 2018 && month == 1 && day == 1);
}

// 部署のややこしい部分の成形
var _createDepartment = function(dep1,dep2){
  if(dep1 == "" && dep2 == ""){
    return "";
  }else if(dep1 == "" ){
    return dep2+"\n";
  }else if(dep2 == "" ){
    return dep1+"\n";
  }else{
    return dep1 + " " + dep2+"\n";
  }
};

// 名前のややこしい部分の成形
var _createToName = function(name,keisyo){
  var work = (keisyo != "" && keisyo != undefined)?keisyo:"様";
  return (name != "" && name != undefined)?name+work+"\n":"";
};

// htmlファイルの読み込み
var _include = function(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

Qiita用に多少手直ししててデバッグしてないのでこのままだとバグるかも(爆
でも雰囲気はバッチリ。

GASのプロジェクトの画面的には下記のようになっているはず。

プロジェクト.png

トリガー(タイマー)の設定

GASのプロジェクト画面で
編集 > 現在のプロジェクトのトリガー
をクリックして、下図のように設定。

トリガー.png

今時のGASは特定の時間指定まで出来ちゃうんですね。

いざ送信

上の一覧の例だと、こんな感じのメールが送信される。

株式会社拳王
世紀末部 覇者課
ラオウ様


明けましておめでとうございます。
以下略

株式会社◯◯◯
ケンシロウ

追伸
今年も待っていてね

終わりに

送信済みのメールは、送信者のGmailの送信済みメールからもちゃんと確認可能。
バウンスは、Gmail APIから見ると送信成功した後の話でハンドリング出来ない(たぶん)ため、『送信済み一覧』から吸い上げる等の別ロジックを用意しないといけなさそう…

件数が多くGASの5分制約を超えそうな場合でも、上のロジックだと複数個時間をずらしてトリガー設定しておけば重複無しで送られるので便利。

難点を挙げれば、送信者はGASの実行者でないといけない制約があるため、このシートを活用したい場合は各送信者がトリガーの設定をしないといけないところ。
また、初回実行時はGmailなどの実行許可ポップアップが出るので、利用者には事前にテストメソッドを実行して許可してもらっておかないとたぶん処理が途中で止まる。

そこさえ注意すれば来年もまた活用出来そう。