📧

GASでSpread sheet上のメアドリストへ自動でメール送信したい

2024/09/28に公開

はじめに

「メアドリストに自動で添付ファイル付きのメール送信できないかな?」と言われたのでGAS初心者なりに頑張ってみた。 スプレッドシートのメーリングリスト上にあるメアドへ Google Documentsの内容を送る構成にした。

セットアップ

1. 必要なもの

  • Google Account
  • Google Spread Sheet
  • Google Documents
  • Google Drive(ファイルを添付するなら)

2. Spread Sheetの準備

  1. 使いたいGoogle Accountで新規Spread Sheetを用意する
  2. 以下の画像の通りに編集する。
    settings
    文字通りに書くだけ
  3. 編集したシートの名前をsettingにする
  1. 新しいシートを追加し、メールリストを以下の画像のように追加する
    maillist
    メールと名前はもちろん本物を
  2. 追加したリストに名前(ex. mail_list1)を付ける
    sheettab
    下のほうはこんな感じになってるはず

3. Apps Scriptの準備

  1. メニューバーから [拡張機能]-> [Apps Script]をクリック
  1. コード.gsというファイルがひらかれている(はず)なので、そこに以下のコードを貼りつける。
    (注釈:このコードでは大量に送信する場合API制限にひっかかる場合がある。API制限を極力回避したプログラムは追記に書いた。)
autosend.js
function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("Auto Send").addItem("Send Emails", "sendEmails").addToUi();
}

function sendEmails() {
  const ss = SpreadsheetApp.getActive();
  const setting = ss.getSheetByName("setting");

  const listName = setting.getRange(2, 1).getValue();
  const title = setting.getRange(2, 2).getValue();
  const documentUrl = setting.getRange(2, 3).getValue();

  let file = getAttachments(setting);

  let from_email = setting.getRange(2, 7).getValue();

  if (from_email != "") {
    let aliases = GmailApp.getAliases();
    if (!aliases.includes(from_email)) {
      Logger.log("Invalid from email address:" + from_email);
      return;
    }
  } else {
    from_email = Session.getActiveUser().getEmail();
  }

  const list = ss.getSheetByName(listName);

  const body = DocumentApp.openByUrl(documentUrl).getBody().getText();

  const lastRow = list.getLastRow();

  for (let i = 2; i <= lastRow; i++) {
    const address = list.getRange(i, 1).getValue();
    const name = list.getRange(i, 2).getValue();

    try {
      sendEmail(address, name, title, body, file, from_email);
      Logger.log("Email sent to " + address);
    } catch (error) {
      Logger.log(
        "Failed to send email to " + address + " Error:" + error.message,
      );
    }
  }
}

function getAttachments(setting) {
  let file = [];
  for (let i = 4; i <= 6; i++) {
    if (setting.getRange(2, i).getValue() != "") {
      let fileId = setting
        .getRange(2, i)
        .getValue()
        .match(/[-\w]{25,}/);
      try {
        let blob = DriveApp.getFileById(fileId).getBlob();
        file.push(blob);
      } catch (error) {
        Logger.log("File not found:" + fileId + " Error:" + error.message);
      }
    }
  }
  return file;
}

function sendEmail(address, name, title, body, file, from_email) {
  let emailBody = body.replace("{{name}}", name);
  GmailApp.sendEmail(address, title, emailBody, {
    attachments: file,
    from: from_email,
  });
}

initial_file
ここに

coded_file
こう!

  1. プロジェクトの名前を適当に変更して(ex. mail_autosend)、保存(ctrl+s)

  2. 左にあるツールバー(画像参照)からトリガー(時計のマーク)を開く。
    trigger
    青くなっているアイコン

  3. 右下の「トリガーを追加」をクリック。下の画像と同じになるように設定を変更し、保存。
    add_trigger
    多分最初からこうなってる

  1. トリガーを保存し、追加されてるのを確認する。
    trigger_assigned
    こんなかんじになってたらOK

  2. Spread Sheetに戻り、一度再読み込みする。メニューバーにAuto Sendが追加されていれば成功
    mailbar
    こんな感じになってるはず

使い方

  1. メール本文を同じアカウントで開いたGoogle Documentsで書く。
  1. 書きおわったらドキュメントのURLをスプレッドシート上の本文URLの下に貼る。
    ss1
  1. メールを送りたい人達のリストを書いたシートを選び(ex. mail_list1)、使用するリストの下に書く

  2. メールのタイトルをタイトルの下に書く。

ss2
ここまでの作業をするとこんなかんじになるはず

  1. メニューバーの[Auto Send] -> [Send Emails]でメールが送られる

以上!

コードの中身

追記予定
といっても簡単なのであまり書くこともない......

最後に

ここにコードのgithubを貼っておく。なにかご不満があればここまで。

追記

どうも一日に送れるメールの数が限られているらしい。他のブログを参照してAPI制限を回避できるらしいコードを書いてみた(動作未保証)

autosend_draft.js
function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("Auto Send").addItem("Send Emails", "sendEmails").addToUi();
}

function sendEmails() {
  const ss = SpreadsheetApp.getActive();
  const setting = ss.getSheetByName("setting");

  const listName = setting.getRange(2, 1).getValue();
  const title = setting.getRange(2, 2).getValue();
  const documentUrl = setting.getRange(2, 3).getValue();

  let file = getAttachments(setting);

  let from_email = setting.getRange(2, 7).getValue();

  let aliases = GmailApp.getAliases();
  if (!aliases.includes(from_email)) {
    Logger.log("Invalid from email address:" + from_email);
    return;
  }

  const list = ss.getSheetByName(listName);

  const body = DocumentApp.openByUrl(documentUrl).getBody().getText();

  const lastRow = list.getLastRow();

  for (let i = 2; i <= lastRow; i++) {
    const address = list.getRange(i, 1).getValue();
    const name = list.getRange(i, 2).getValue();

    try {
      sendEmail(address, name, title, body, file, from_email);
      Logger.log("Email sent to " + address);
      Utilities.sleep(1000);
    } catch (error) {
      Logger.log(
        "Failed to send email to " + address + " Error:" + error.message,
      );
    }
  }
}

function getAttachments(setting) {
  let file = [];
  for (let i = 4; i <= 6; i++) {
    if (setting.getRange(2, i).getValue() != "") {
      let fileId = setting
        .getRange(2, i)
        .getValue()
        .match(/[-\w]{25,}/);
      try {
        let blob = DriveApp.getFileById(fileId).getBlob();
        file.push(blob);
      } catch (error) {
        Logger.log("File not found:" + fileId + " Error:" + error.message);
      }
    }
  }
  return file;
}

function sendEmail(address, name, title, body, file, from_email) {
  const draft = makeDraft(address, name, title, body, file, from_email);
  const draftId = draft.getId();
  GmailApp.getDraft(draftId).send();
}

function makeDraft(address, name, title, body, file, from_email) {
  const emailBody = body.replace("{{name}}", name);
  const draft = GmailApp.createDraft(address, title, emailBody, {
    attachments: file,
    from: from_email,
  });
  return draft;
}
  1. API制限を回避するために下書きを一度経由する(!)
  2. 一秒間に一回送るように制限する

の2つをした。 一回あたり90件ぐらい送るとApps Scriptの実行時間制限がくるのでその都度リストを更新するのがちょっと大変(送信ログはApps Scriptの[実行数]から一番上のログの▽を押すと見られる。送信ログは最新ではないので、自分のメールからちゃんと送信できてるか確認したほうがよさそう)

トラブルシューティング

送信履歴をみるとエラーを吐いているメアドがある!

参考

https://daily-coding.com/mail-with-files/
https://jp.tdsynnex.com/blog/google/options-sending-email-by-gas/
https://zenn.dev/tatsuya_okzk/articles/259203cc416328

GitHubで編集を提案

Discussion