新年のご挨拶(年賀状)メールを一斉に送信するGoogle Apps Script(GAS)を作ってみた

2022年も年の瀬となりました。新年のご挨拶の準備として人によっては年賀状作りや投函に勤しんでいる方も多いと思います。普段交流があまりない人であっても毎年の年賀状は送り合っているという人も多いと思います。
近年では新年のご挨拶は年賀状を送るのではなく、メールやLINEなどのSNSでのメッセージを送ることで新年のご挨拶をしている方も増えてきています。
そこで今回、もうすぐ年があけたときに行う新年のご挨拶メールを一斉に送れるようなGoogle Apps Script(GAS)を作ってみましたので紹介します。
実際に作成してみたプロジェクトはこちらになります

[BulkGreeter](https://github.com/TakuKobayashi/BulkGreeter)

※ 作成したプロジェクトでは

* [clasp](https://github.com/google/clasp)
* Typescript

を使ってGASの開発つを行なっています

# 連絡先一覧の情報の作成

メールアドレスの連絡先のリストは`csv`や`xls`の形で抽出することができると一括メール送信の処理を作りやすいです。
今回は名刺をいただいた人たちに新年のご挨拶メールを一斉に送信することを想定してデータの抽出(作成)を行なっていきます。

## 名刺管理サービス(連絡先管理サービス)

* [Eight](https://8card.net/)
* [CAMCARD](https://www.camcard.jp/)
* [myBridge](https://jp.mybridge.com/home)

など

もらった名刺や連絡先をオンラインで管理できるサービスが数多くあります。
これら名刺登録サービスにもらった名刺を読み込んで管理していくと非常に便利です。
数あるオンライン名刺管理サービスを利用しましたが個人的には[myBridge](https://jp.mybridge.com/home)が使いやすく便利かつ、連絡先情報をファイルにエクスポートすることが無料でできたので、今回は[myBridge](https://jp.mybridge.com/home)を使って連絡先情報をエクスポートしていきたいと思います。

### [myBridge](https://jp.mybridge.com/home)より連絡先をエクスポートする

1.[myBridge](https://jp.mybridge.com/home) のアプリを開き、「メニュー」を選択し、「ファイルでエクスポート」を選択する

![myBridge-menu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/6e99293d-006a-f118-6e54-4f19cf9f42c2.png)

2.メールアドレスを入力し、「送信」を選択する

![myBridge-export-file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/9b6b1c67-1c09-fd81-577b-13653da98552.png)

3.エクスポートするファイル形式は`xls` 以外にも選択可能

![myBridge-select-file-type.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/c6a90b7c-7f96-4d02-d9cf-1684152e0c05.png)

### エクスポートしたファイルをGoogle Driveにアップロードして確認する

1.[myBridge](https://jp.mybridge.com/home)よりエクスポートしたファイルがメールで送られてくる

![exported-contact-file-from-mail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/bf438930-7243-99e5-d246-bd082d9616fa.png)

2.送られてきたファイルをGoogle Driveにアップロードする

![myBridge-export-spread-sheet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/8abcf30a-8691-48c8-18b9-c720e28d6f74.png)

3.Google DriveにあげたファイルをGoogle Spreadsheetに展開して中身を確認する

![myBridge-export-spread-sheet-data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/01885897-c838-cde6-5923-8e76f4e22070.png)

※ 上記の画像はテスト用のために数を絞っています

この中身の情報をGASで参照しながらメールを送っていく処理を作成していきます

# メールを送信する

上記の連絡先が記録されたSpreadSheetを参照し、そこに記載されているメールアドレスに対してメールを送信するGASを作成しました。
GASの処理の内容については以下に記述を参考にしてください。
[bulk-send-mail.ts](https://github.com/TakuKobayashi/BulkGreeter/blob/master/src/bulk-send-mail.ts)

## サンプルプロジェクトを使用する場合に変更すべき場所

* [.clasp.json](https://github.com/TakuKobayashi/BulkGreeter/blob/master/.clasp.json) の `scriptId` → ここに記述されている `scriptId` は各々のアカウントで作成されるGASプロジェクトの値になるので、その値を設定するようにしてください
* [const contactSheerUrl = 'SpreatSheetのURL'](https://github.com/TakuKobayashi/BulkGreeter/blob/master/src/bulk-send-mail.ts) → 参照するSpreadSheetのURLは人によって異なるものであるので、自身の連絡先のリストのSpreadSheetのURLをここでは設定するようにしてください

* [const attachmentFileBlob = DriveApp.getFileById('Google DriveのファイルID').getBlob()](https://github.com/TakuKobayashi/BulkGreeter/blob/master/src/bulk-send-mail.ts) → メールに添付するファイルは人によって異なるものであるので、添付したいファイルをアップしたGoogle DriveのファイルIDを設定するようにしてください

### scriptId を確認する方法

1.GASのプロジェクトを開き、「プロジェクトの設定」を選択する

![GAS-project-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/17d62510-8ade-4e4f-4bd0-3186f07a14c3.png)

2.その中に記載されている「スクリプトID」を確認して、コピーして記述する

![check-gas-script-id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/e4e0ea20-be43-d56f-c05b-0a1687fd7d44.png)

### Google DriveのファイルID を確認する方法

1.Google Driveにてファイルを開き、「共有」を選択する

![google-drive-share-id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/87ecc3a4-fa41-087f-cd33-a29949088230.png)

2.出てきたポップアップの「リンクをコピー」を選択する

![google-drive-share-link-copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/e69795aa-92d6-a899-04ca-3ce6341ebf0d.png)

3.`https://drive.google.com/file/d/GoogleDriveファイルID/view` このようなURLがコピーされるのでこの中の`GoogleDriveファイルID` の部分がGoogleDriveファイルIDにあたります。
 例えば、上記をコピーした場合 https://drive.google.com/file/d/1L1OT_BXZoV0Dpsc2T88wp38jcpBYFsue/view のURLを取得できたので、`1L1OT_BXZoV0Dpsc2T88wp38jcpBYFsue` この部分が`Google DriveのファイルID`にあたります。

![google-drive-file-id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/506605dc-8639-dcfd-3032-dd53e9cd4e2e.png)

## サンプルプロジェクトのGASへの反映と実行

このプロジェクトの内容をGASに反映させる場合は
[clasp](https://github.com/google/clasp)をインストールし

```
npm install -g @google/clasp
```

以下のコマンドを実行してください

```
clasp push
```

あるいは[yarn](https://yarnpkg.com/)がすでにインストールされている場合には

```
yarn run push
```

でGASに反映されます。
後述のタイマーで実行される場合にはdeployを行う必要があるので

```
clasp deploy
```

または

```
yarn run deploy
```

を行なってください。

反映されたGASプロジェクトを以下のコマンドを実行して開きます

```
clasp open
```

または

```
yarn run open
```

実行したい関数名を選択して(基本的に`bulkSendMail`を実行する)「実行」を押して実行することで実行されます

![execute-gas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/fb07354f-dae7-bf6b-ca50-b7fe614a647d.png)

## 処理の解説

### Google SpreadSheetから情報を取得する

以下のような `function` を作成しました

```typescript
function loadContactsFromSheet(): { [key: string]: any }[] {
  const targetSpreadSheet = SpreadsheetApp.openByUrl("SpreadSheetのURL");
  const targetSheets = targetSpreadSheet.getSheets();
  // シートは一つしかないので、一つのシートを指定する
  const targetSheet = targetSheets[0];
  const contactDataObjs: { [key: string]: any }[] = [];
  if (targetSheet) {
    const lastColumnNumber = targetSheet.getLastColumn();
    const lastRowNumber = targetSheet.getLastRow();
    // 1行目に記載されているheader情報だけをまずは取得する
    const headerDataRange = targetSheet.getRange(1, 1, 1, lastColumnNumber);
    const headerData = headerDataRange.getValues();
    if (headerData.length > 0) {
      // headerを除いた2行目以降のCellデータ全部を取得する
      const contactDataRange = targetSheet.getRange(2, 1, lastRowNumber - 1, lastColumnNumber);
      const contactData = contactDataRange.getValues();
      for (const rowData of contactData) {
        // 1行分のデータ
        const rowContactDataObj: { [key: string]: any } = {};
        // header情報をkey、そのkeyに対する値を保持する
        headerData[0].forEach((header, columnIndex) => {
          rowContactDataObj[header.toString()] = rowData[columnIndex];
        });
        contactDataObjs.push(rowContactDataObj);
      }
    }
  }
  return contactDataObjs;
}
```

この `function` を `loadContactsFromSheet()` のように呼び出すことによってSpreadSheetの情報を読み込み、扱いやすいように整形して取得できるようにしています

つまりSpreatSheetの内容はこのような形なので

![myBridge-export-spread-sheet-data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/01885897-c838-cde6-5923-8e76f4e22070.png)

`loadContactsFromSheet()` を実行した結果取得できる値は以下のようなObujectの配列に変換されたものになります。

```plantuml
@startjson
[
  {
    "会社名":"〇〇株式会社",
    "名前":"小林拓",
    "部署":"システム部",
    "役職":"",
    "電子メール":"ooooo@xxxx.co.jp",
    "郵便番号":"1410031",
    "会社住所":"東京都品川区〇〇-△△-□□",
    "会社電話":"03-XXXX-XXXX",
    "会社FAX":"03-XXXX-XXXX",
    "携帯電話":"",
    "名刺交換日":"2020年12月29日",
    "グループ":"",
    "メモ":""
  }
]
@endjson
```

### Gmailにてメールを送信する

取得したSpreadSheetの情報を基にその中にある全てのメールアドレスに対してメールを送る `function` を作成する

```typescript
function bulkSendMail() {
  const contactDataObjs = loadContactsFromSheet();
  for (const contactData of contactDataObjs) {
    // メールアドレスが設定されている場合のみメールを送る
    if (contactData['電子メール']) {
      sendMail(contactData);
    }
  }
}

function sendMail(contactObj: { [key: string]: any }) {
  const subject = '謹賀新年'; // 件名
  const headerGreetLine = [contactObj['会社名'], contactObj['名前'], '様'].join(' ');
  const body = [headerGreetLine, 'あけましておめでとうございます', '昨年は大変お世話になりました','今年もよろしくお願いします'].join('\n'); // メールの本文
  const options = {};
  GmailApp.sendEmail(contactObj['電子メール'], subject, body, options);
}
```

上記の`function`の`sendMail(contactData)` を呼び出すことによってメールを送信することができます。
メール本文に相手の名前などの変数を用いることによって(`contactObj['会社名']`や`contactObj['名前']`に相当)、それぞれの人に宛てた本文を作成することができます。
SpreadSheetに記載されている連絡先全てにメールを送る場合には `bulkSendMail()` の`function` を実行します。

### メールにファイルを添付して送信する

新年のご挨拶メールを送る場合、文章のみでなく年賀状のように画像などのファイルを添付して送信することが多くあります。
画像などのファイルを添付してメールを送る場合、そのファイルを事前にGoogle Driveにアップロードしておきます。
そして上記のメール送信のGASにファイルを添付して送ることができるように以下のように修正します。

```typescript
// ファイルの読み込みを何回も行わないためにあらかじめ読み込んでおく
const attachmentFileBlob = DriveApp.getFileById('Google DriveのファイルID').getBlob();

function sendMail(contactObj: { [key: string]: any }) {
  const subject = '謹賀新年'; // 件名
  const headerGreetLine = [contactObj['会社名'], contactObj['名前'], '様'].join(' ');
  const body = [headerGreetLine, 'あけましておめでとうございます', '昨年は大変お世話になりました','本年もよろしくお願いいたします'].join('\n'); // メールの本文
  const options = {
    attachments: attachmentFileBlob,
  }; // ファイルを添付する
  GmailApp.sendEmail(contactObj['電子メール'], subject, body, options);
}
```

### メール本文に画像を組み込んだメール

ファイルの添付ではなくメール本文中に画像を挿入して表示するにはHTMLメールにしてインライン画像を含めることによってメールを送ることができます。
そして上記のメール送信のGASにファイルを添付して送ることができるように以下のように修正します。

```typescript
// ファイルの読み込みを何回も行わないためにあらかじめ読み込んでおく
const attachmentFileBlob = DriveApp.getFileById('Google DriveのファイルID').getBlob();

function sendMail(contactObj: { [key: string]: any }) {
  const subject = '謹賀新年'; // 件名
  const headerGreetLine = [contactObj['会社名'], contactObj['名前'], '様'].join(' ');
  const body = [headerGreetLine, 'あけましておめでとうございます', '昨年は大変お世話になりました','本年もよろしくお願いいたします', '<img src="cid:inlineImg">'].join('<br>'); // メールの本文
  const options = {
    htmlBody: body,
    inlineImages: {
      inlineImg: attachmentFileBlob,
    },
  }; // ファイルを添付しつつ本文の中にインライン画像を埋め込む
  GmailApp.sendEmail(contactObj['電子メール'], subject, body, options);
}
```

※ Gmailではこの画像が挿入されたHTMLメールを表示することができますが、Gmail以外のメールサービスではうまく表示されない場合もあります。そのため上記のようなインライン画像を含めたHTMLメールを送信する方法はあまりお勧めしません。

### その他メール送信におけるカスタマイズ

送信元の指定など、メールを送信するときに各種細かい指定も行うことができます。詳しくは[こちら](https://developers.google.com/apps-script/reference/gmail/gmail-app#sendemailrecipient,-subject,-body,-options)より確認してください。
また件名や本文についても適宜変更してみてください。

# タイマーを設定して自動的に送信

GASスクリプトより一括でメールを送れる準備が整ったら、メールが自動的に送信されるように以下のようにタイマーを設定することもできます。

1. 「トリガー」を選択し、「トリガーの追加」を行う

![move-trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/d2c30769-f030-1b20-127e-82abe0724fbd.png)

2. 実行する関数を `bulkSendMail` に指定し、「時間手動型」の「特定の日時」をそれぞれ指定して日付を入力して「保存」します

![save-trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/7074801c-5508-44b3-49d6-679c2ba1aa24.png)

無事に保存されれば、時間になったら一斉メール送信が実行されます

# 最後に

それではよいお年を!!