【GAS】休暇メンバーをカレンダーから自動取得しGmailで通知する

# この記事はなにか

会社などで本日休暇メンバーをカレンダーから自動取得＆メール共有する`GoogleAppsScript`です。
導入・管理しやすさに重点を置き、下記ポイントに留意しました(´・ω・`) 

- スクリプトなどは基本コピペでOK！
- メール送信先、カレンダー取得先の追加や削除はスプレッドシート上でカンタン管理！
- 定時に送付、土日祝日は送らない！（←[他の方の記事](https://qiita.com/jz4o/items/d4e978f9085129155ca6)を参考にしました）

■メール本文イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/edac07b8-8330-9c15-aba1-a0b9e6b69384.png)

# 作成・準備

①準備するもの

- Googleアカウント
- Gmail
- Googleカレンダー（「他のカレンダー」に取得したいメンバーを登録しておく必要あり）
- スプレッドシート

②導入までの流れ

1. スプレッドシート準備
- スクリプト記述
- カレンダー登録
- 稼働確認
- トリガー作成
- 運用

## 1.スプレッドシート準備

コードにも出てきていますが、今回は2つのシートを準備します。

1. メール送信先
- カレンダー取得先

※コードで上記の名称を使用しているので、シート名はその通りにしてください。

シートのイメージはこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/e8074329-e506-cb0c-7376-80720549f378.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/2b67f514-b737-8414-7472-015b77267378.png)

それぞれのシートの`A列に名前、B列にアドレスもしくはカレンダーID（基本は同じ）`を入力します。
この時、最終行までは自動で取得しますが、その間に空欄や打ち間違いがあるとエラーとなります。
**必要な行以降は書かないほうが無難です。エラーの原因となります。**

## 2.スクリプト記述

スプレッドシート→ツール→スクリプトエディタの順にクリックし、
スクリプトエディタに下記ソースを記述します。

```js
//メインで実行する関数
function notifyGmail(){
  
  var holiday = isHoliday();

  if (isHoliday() == false){

    var recipients = getRecipients();
    var holidayMembers = getCalendar();
    var mailBody = '■■このメールはシステムからの自動配信です■■\n\n本日、以下の方が休暇となっています。\n\n【休暇メンバー】\n';
    
    if (holidayMembers.length == 0){
      
      mailBody += 'なし\n';
      
    } else{
      
      mailBody += '';
      
      holidayMembers.forEach(function(holidayMember){
        mailBody += '・' + holidayMember + '\n';  
      });
        
    }

    mailBody += '\n※スケジュールに「休」もしくは「有給」の文字が入っている方を休暇と判定しています。\n詳細は各自のカレンダーをご確認ください。\n\n\nFrom お休みの人教えちゃうbot (・∀・)' 
    
    //メールの作成
    GmailApp.sendEmail(recipients, '【送付】本日の休暇メンバー', mailBody);
    
  }
  
}

//カレンダーから休みのメンバーリストを取得
function getCalendar() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var calendarMembersSheet = ss.getSheetByName("カレンダー取得先");
  var members = calendarMembersSheet.getRange(2, 1, calendarMembersSheet.getLastRow() - 1).getValues();
  var Ids = calendarMembersSheet.getRange(2, 2, calendarMembersSheet.getLastRow() - 1).getValues();
  var calendarIds = {};
  for (var i = 0, l = members.length; i < l; i++) {
    calendarIds[members[i]] = Ids[i];
    
  }

  var holidayMembers = [];
  var myCalendars = [];
  
  members.forEach(function(member){
    
    var calendarId = calendarIds[member];
    var myCalendar = CalendarApp.getCalendarById(calendarId);
    var memberName = myCalendar.getName();
    var startDate = new Date();
    var myEvents = myCalendar.getEventsForDay(startDate);

    myEvents.some(function(myEvent){
      var title = myEvent.getTitle();
      //カレンダーの条件が「休」「有給」以外に必要な場合はこちらに追加↓
      if(title.match(/休/) || title.match(/有給/)){
        holidayMembers.push(memberName + 'さん（' + title + '）');
        //「カレンダー取得先」シートのA列で指定した名前が良い場合は上を削除してこちらを使用↓
        //holidayMembers.push(member + 'さん（'　+ title + '）');
        return true;
      }
    });
    
  });
  
  return holidayMembers;
  
}

//送付先メンバーリストを取得
function getRecipients(){
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var recipientsSheet = ss.getSheetByName("メール送信先");
  recipientsSheet.activate();
  var recipients = recipientsSheet.getRange(2, 2, recipientsSheet.getLastRow() - 1).getValues();
  return recipients;
  
}

//土日祝日を判定するスクリプト
//参考URL...https://qiita.com/jz4o/items/d4e978f9085129155ca6
function isHoliday(){
  
  var today = new Date();

  //土日か判定
  var weekInt = today.getDay();
  if(weekInt <= 0 || 6 <= weekInt){
    return true;
  }

  //祝日か判定
  var calendarId = "ja.japanese#holiday@group.v.calendar.google.com";
  var calendar = CalendarApp.getCalendarById(calendarId);
  var todayEvents = calendar.getEventsForDay(today);
  if(todayEvents.length > 0){
    return true;
  }

  return false;
  
}

```


## 3.カレンダー登録

ここが忘れがちなポイントです。
実際にやってみたところ、カレンダーIDをスプレッドシートに記述するだけではエラーになってしまったため、
`取得先のカレンダーは「他のカレンダー」に追加`しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/3c12eef5-c4ad-0951-4e9c-dfcd954ef71b.png)

## 4.稼働確認

ここまでくれば稼働確認です。
関数で`notifyGmail`を選択し、実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/6717a53e-b5e0-f288-bd8c-e321623c9b11.png)

特にエラーが起きず、以下のようなメールが受信できればOKです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/edac07b8-8330-9c15-aba1-a0b9e6b69384.png)

## 5.トリガー作成

今回、トリガーは毎朝出勤前に来てればいいかなと思ったので以下のように設定しました。
ちなみに、[時間ぴったりに実行できる方法](https://qiita.com/sumi-engraphia/items/465dd027e17f44da4d6a)もあるみたいです。
土日祝日のメール送信回避はスクリプト上で設定してあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/907b4a2c-b35b-96f8-de5e-e2c3b8814978.png)

## 6.運用

以上で設定は完了です。
1週間くらい自ら運用してみて、大丈夫そうだったら仲間にも送ってあげましょう！
電話番などで毎度カレンダーを確認する手間が少しは省けるかと笑

GAS、いろんな機能との連携ができて楽しいですね！
できなかったり、不明点あればコメントかメッセージをお願いします。

# 追記（HTML化してカレンダーのアイコンをクリックしたらカレンダーに飛ぶようにしたい）

そのうち別記事で書こうと思っていたのですが、なかなか腰が上がらないためこちらに記載します。

イメージはこんな感じで、カレンダーのアイコンをクリックしたらGoogleカレンダーに飛びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224314/a9143117-50b4-7cd5-7121-90ea6563d0f0.png)


以下、スクリプトです。

```.javascript
function notifyGmail(){
  
  var holiday = isHoliday();
  
  if (isHoliday() == false){
  
    var recipients = getRecipients();
    var holidayMembers = getCalendar();
    var mailBody = '■■このメールはシステムからの自動配信です■■<br><br>本日、以下の方が休暇となっています。<br><br>【休暇メンバー】<br>';
    
    if (holidayMembers.length == 0){
      
      mailBody = 'なし<br>';
    
    } else{
      
      mailBody += '';
      
      holidayMembers.forEach(function(holidayMember){
        mailBody += '・' + holidayMember + '\n';  
      });
        
    }

    mailBody += '<br>※スケジュールに「休」もしくは「有給」の文字が入っている方を休暇と判定しています。<br>詳細は各自のカレンダーをご確認ください。<br><br><br>From お休みの人教えちゃうbot (・∀・)' 
    
    GmailApp.sendEmail(recipients, '【送付】今日の休暇メンバー', '' ,{htmlBody: '<p>' + mailBody + '</p>'} );
    
  }
  
}

function getCalendar() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var calendarMembersSheet = ss.getSheetByName("カレンダー取得先");
  var members = calendarMembersSheet.getRange(2, 1, calendarMembersSheet.getLastRow() - 1).getValues();
  var Ids = calendarMembersSheet.getRange(2, 2, calendarMembersSheet.getLastRow() - 1).getValues();
  var calendarIds = {};
  for (var i = 0, l = members.length; i < l; i++) {
    calendarIds[members[i]] = Ids[i];
    
  }

  var holidayMembers = [];
  var myCalendars = [];
  
  members.forEach(function(member){
    
    var calendarId = calendarIds[member];
    var myCalendar = CalendarApp.getCalendarById(calendarId);
    var memberName = myCalendar.getName();
    var startDate = new Date();
    var myEvents = myCalendar.getEventsForDay(startDate);
    
    myEvents.some(function(myEvent){
      var title = myEvent.getTitle();
      if(title.match(/休/) || title.match(/有給/)){
        holidayMembers.push(memberName + 'さん（' + title + '）' + '<a href= "https://calendar.google.com/calendar/embed?src=' + calendarId[0].replace('@', '%40') + '&ctz=Asia%2FTokyo">&#x1f4c5;</a><br>');
        //「カレンダー取得先」シートのA列で指定した名前がいい場合はこちら↓
        //holidayMembers.push(member + 'さん（'　+ title + '）'+ '<a href= "https://calendar.google.com/calendar/embed?src=' + calendarId[0].replace('@', '%40') + '&ctz=Asia%2FTokyo">&#x1f4c5;</a><br>');
        return true;
      }
    });
    
  });
  
  return holidayMembers;
  
}

function getRecipients(){
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var recipientsSheet = ss.getSheetByName("メール送信先");
  recipientsSheet.activate();
  var recipients = recipientsSheet.getRange(2, 2, recipientsSheet.getLastRow() - 1).getValues();
  return recipients;
  
}

//土日祝日を判定するスクリプト
//参考URL...https://qiita.com/jz4o/items/d4e978f9085129155ca6
function isHoliday(){
  
  var today = new Date();

  //土日か判定
  var weekInt = today.getDay();
  if(weekInt <= 0 || 6 <= weekInt){
    return true;
  }

  //祝日か判定
  var calendarId = "ja.japanese#holiday@group.v.calendar.google.com";
  var calendar = CalendarApp.getCalendarById(calendarId);
  var todayEvents = calendar.getEventsForDay(today);
  if(todayEvents.length > 0){
    return true;
  }

  return false;
  
}
```


リファクタ版（WIP）

```js
function notifyGmail(){

  if (!isHoliday()){

    let recipients = getRecipients();
    let holidayMembers = getCalendar();
    let mailBody = '■■このメールはシステムからの自動配信です■■<br><br>本日、以下の方が休暇となっています。<br><br>【休暇メンバー】<br>';

    if (holidayMembers.length === 0){
      mailBody = '本日、予定のある方はいません。<br>';
    }else{
      mailBody += '';
      holidayMembers.forEach(function(holidayMember){
        mailBody += '・' + holidayMember + '\n';
      });
    }
    mailBody += '<br>※スケジュールに「休」もしくは「有給」の文字が入っている方を休暇と判定しています。<br>詳細は各自のカレンダーをご確認ください。<br><br><br>From お休みの人教えちゃうbot (・∀・)'

    //メールを送信する
    GmailApp.sendEmail(recipients, '【送付】今日の休暇メンバー', '' ,{htmlBody: '<p>' + mailBody + '</p>'} );
  }
}

function getCalendar() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const calendarMembersSheet = ss.getSheetByName("カレンダー取得先");
  const members = calendarMembersSheet.getRange(2, 1, calendarMembersSheet.getLastRow() - 1).getValues();
  const Ids = calendarMembersSheet.getRange(2, 2, calendarMembersSheet.getLastRow() - 1).getValues();
  var calendarIds = {};
  for (var i = 0, l = members.length; i < l; i++) {
    calendarIds[members[i]] = Ids[i];
  }

  var holidayMembers = [];
  var myCalendars = [];

  members.forEach(function(member){
    let calendarId = calendarIds[member];
    let myCalendar = CalendarApp.getCalendarById(calendarId);
    //エラー確認用
    try{
      let memberName = myCalendar.getName();
    }catch(e){
     let result = "カレンダーID:" + calendarId + "<br>エラー内容:" + e + "<br>対象のカレンダーIDが存在するか、登録されているかを確認してください。";
     console.log(result);
     //エラー通知用（退職でアドレスが消された時など）。'XXX@gmail.com'部分には管理者のアドレスを入れてください。
     GmailApp.sendEmail('XXX@gmail.com', '【エラー通知】今日の休暇メンバー', '' ,{htmlBody: '<p>' + result + '</p>'} );
    }
    var memberName = myCalendar.getName();
    var startDate = new Date();
    var myEvents = myCalendar.getEventsForDay(startDate);

    myEvents.some(function(myEvent){
      var title = myEvent.getTitle();
      if(title.match(/休/) || title.match(/有給/)){
        holidayMembers.push(memberName + 'さん（' + title + '）' + '<a href= "https://calendar.google.com/calendar/embed?src=' + calendarId[0].replace('@', '%40') + '&ctz=Asia%2FTokyo">&#x1f4c5;</a>');
        //「カレンダー取得先」シートのA列で指定した名前がいい場合はこちら↓
        //holidayMembers.push(member + 'さん（'　+ title + '）');
        return true;
      }
    });
  });
  return holidayMembers;
}

function getRecipients(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const recipientsSheet = ss.getSheetByName("メール送信先");
  recipientsSheet.activate();
  const recipients = recipientsSheet.getRange(2, 2, recipientsSheet.getLastRow() - 1).getValues();
  return recipients;
}

//土日祝日を判定するスクリプト
//参考URL...https://qiita.com/jz4o/items/d4e978f9085129155ca6
function isHoliday(){
  const today = new Date();

  //土日か判定
  var weekInt = today.getDay();

  if(weekInt <= 0 || 6 <= weekInt)
    return true;

  //祝日か判定
  const calendarId = "ja.japanese#holiday@group.v.calendar.google.com";
  const calendar = CalendarApp.getCalendarById(calendarId);
  const todayEvents = calendar.getEventsForDay(today);

  if(todayEvents.length > 0)
    return true;
  return false;

}
```

# 参考
[Apps Script | Google Developers](https://developers.google.com/apps-script)
[ビジネスパーソンに贈る業務効率化大全10選 〜Google Apps Scriptによる業務の自動化〜｜Udemy](https://www.udemy.com/course/gas_for_operational_efficiency/)
[【GAS】休日・祝日を判定する関数｜Qiita](https://qiita.com/jz4o/items/d4e978f9085129155ca6)