【ヒノ僕】(ヒューマンエラーが多いノンプログラマの僕が)GASでメール送信を自動化した件

#はじめに

みなさんは一日に何通メールを捌きますか？弊社ではSlackを利用しているため、内部でのコミュニケーションでメールを用いることは殆どありませんが、B2Cのサービスではメールを使わざるを得ないシーンってすぐにはなくすことができないんですよね。

とはいえせいぜい一日のメール数なんて10通程度。しかもこの10通って「何て返そうか」と頭悩ませるものというよりは、<b>テンプレートを送っているだけ</b>って感じです。
ただ、人力でメアド間違えたり名前間違えたりって怖いじゃないですか~~(僕は前職でこれでこってり絞られました)~~。ヒューマンエラー避けるためにも、だったら尚の事自動化したらいいやん！っていうのがこの記事を作った背景です。
※今後ヒノ僕シリーズゆっくり増やしていきます

#今日のレシピ
今回はGmailを用いたメールの自動送信をするわけですが、そのために必要なものを以下で簡単に解説していきます。

##Google Apps Script(GAS)を始める
- URLクリックして始めるだけ
- [こちら](https://script.google.com/home)から直アクセスできます
- 環境によっては標準時刻が日本時間じゃないケースもあるので要注意
    - 設定から`apps script.json` を開けるようにして、`time zone` を`Asia/Tokyo`に変更で解決

##ドキュメントを用いたメールの準備 
- GoogleDocumentを利用してとりあえずメールの本文を作る
    - その際、宛先など変化のあるものは`{name}` `{date}`のように記載する（文字列の置換については後述）
- ドキュメントが作成できたらIDを取得し、GASでのプログラミングに移行する
    - ドキュメントIDはドキュメントのURLの中、/d/xxxxxxxx/editの間のxxxxxxxxの部分
- class DocumentAppについて
    - DocumentAppについての詳細は[こちら](https://developers.google.com/apps-script/reference/document/document-app)から
    - Method `openById(documentId)`を用いることで使いたいドキュメントファイルを指定することができます
    - document内のメール本文を取得する際には`getBody()`と`getText()`を利用することでメール本文の取得が可能です
- ドキュメントテキストを置換する
    - ドキュメントを置換するためには`.replace("置換前の文字列","置換後の文字列")`を利用する
        - replece(1).gs参照
    - `replace()`が置換するのは一度のみであるため、複数回置換する場合は工夫が必要
        - replace(2).gs参照

```js:replace(1).gs
function replace() {
  var docText = '私の名前は{name}です';
  console.log(docText);
  //出力結果　→	情報	私の名前は{name}です
  var docText = 
  docText.replace('{name}','ヒノ僕');
  console.log(docText);
  //出力結果　→	情報	私の名前はヒノ僕です
}
```
```js:replace(2).gs
function replace() {
  var docText = '私は{place}に住んでいました。{place}はとってもいいところです。'
  console.log(docText)
  //出力結果　→	情報	私は{place}に住んでいました。{place}はとってもいいところです。

  var docText = 
  docText.replace('{place}','香港')
  console.log(docText)
  //出力結果　→	情報	私は香港に住んでいました。{place}はとってもいいところです。
  //この通り、.replace()は基本的に一回しか使えないが…

  var replaceWord = /{place}/g;
  docText = 
  docText.replace(replaceWord, '香港');
  console.log(docText)
  //私は香港に住んでいました。香港はとってもいいところです。
  //　/置換前の文字列/g で複数回利用するフラグを建てることも可能
}
```
- ここまでできればメールで使える本文は完成！

##Spreadsheetアプリを用いた送信リストの作成
- Spreadsheetでメール送信のリストを作成する
    - 用途によってニーズは変わると思いますが、困ったら~~こちらを参考にしてください~~
- Spreadsheetの作成が完了したらGASでのプログラミングに移行する
- class spreadsheetAppの使い方
    - SpreadsheetAppについての詳細は[こちら](https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app)から
    - Method `openById(spreadsheetId)`を用いることで使いたいシートファイルを指定することができます
    - spreadsheetを指定するだけではシートを特定できないので、`getSheetByName(sheetName)`を用いてシートを特定する
    - シートの中から使う範囲の特定も必須
- ループ処理を用いた動的なシート情報の取得について
    - ループでよく使われるのが`for`を用いる方法
    - 変数`i`を用いることで、一行ずつ、必要な情報を動的に取得することができる

```js:loop.gs
function loop() {
  for (var i = 1; i <= 10; i++){
    console.log(`プログラミング学習${i}日目`)
  }
}
  //出力結果↓
  //情報	プログラミング学習1日目
  //情報	プログラミング学習2日目
  //情報	プログラミング学習3日目
  //             …
  //情報	プログラミング学習10日目
```
- `i`行目のリストを取得するための式を作る
    - 送信リストを取得するために、必要な項目を定義
    - 送信先の名前、メールアドレス、必要に応じた変数諸々(予定の日時、送信未済のチェックなど)
    - 以下、例

```js:list.gs
function readSheet() {
  const spreadsheetId = '************'
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId)
  const sheetName = '+++++++++'
  const sheet = spreadsheet.getSheetByName(sheetName)
  const lastRow = sheet.getLastRow

  for (var i = 2; i <= lastRow; i++) {
    // i が2なのは、1行目の項目を読み取る必要がないから
    var name = sheet.getRange(i, 1).getValue();
    // シートのA列目、今回だと名前を取得
    var address = sheet.getRange(i, 2).getValue();
    // シートのB列目、今回だとアドレスを取得
    var mailSent = sheet.getRange(i, 3).getValue();
    // シートのC列目、今回だとメール送信の未済を取得
  }
}
```
##Gmailアプリを用いたメールの送信
- 本命GmailAppを用いたプログラミングだが、やはりclass`gmailApp`を利用する
    - GmailtAppについての詳細は[こちら](https://developers.google.com/apps-script/reference/gmail/gmail-app)から
    - メール送信で使われるのは主に Method `sendEmail(recipient, subject, body)`
    - 或いは Method `sendEmail(recipient, subject, body, options)`
        - `recipient` はメール送信先の相手のアドレス
        - `subject` はメールタイトル
        - `body` はメール本文
        - `options`では主にCCやBCC、送り主の設定などが可能

#まとめると
```js:sendMail.gs
function sendMail() {

  const spreadsheetId = `************************`;
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  const sheetName = `+++++++++++++++`;
  const sheet = spreadsheet.getSheetByName(sheetName);
  const lastRow = sheet.getLastRow()

  for (var n = 2; n <= lastRow; n++) {
    var name = sheet.getRange(n, *).getValue();
    var recipient = sheet.getRange(n, *).getValue();
    var time = sheet.getRange(n, *).getValue();
    var startTime = new Date(sheet.getRange(n, *).getValue());
    startTime.setHours(time.getHours());
    startTime.setMinutes(time.getMinutes());
    Utilities.formatDate(startTime, "JST", "MM/dd")
    var mailSent = sheet.getRange(n, *).getValue();
    //ここまでで、スプレッドシートから必要な情報を定義

    const subject = '【就活CAMP】次回面談のご案内'
    // メールの差出情報
    const docId = `1bzIYi4f2x1GMq_eFzIX8rDx7xluyR56YSucHk4ednTI`;
    var doc = DocumentApp.openById(docId)
    var docText = doc.getBody().getText();
    var body = docText
      .replace(`{name}`, name)
      .replace(`{date}`, startTime)
    // ドキュメントについて定義。

    if (mailSent == false) {
      MailApp.sendEmail(recipient, subject, body, options)
    }
  }
}
```
##追記
- 上記の式だとあまりにも簡素すぎるので、Spreadsheetから取得する情報や`GmailApp`の内容を充実させるとGood！
- また、メール送信後、送信済みのフラグを立てる仕組みを作ったり、場合分けをして複数のメールを設定するのも面白い