【GAS】Slackで休暇取得者一覧を自動通知する機能を作った

# はじめに
弊社では、「⚪︎日は休暇で不在ですよー:whale2:」とお知らせするSlackチャンネルがあります。
（正式な休暇申請のシステムとは別です）
この情報をワークフローで取得し、休暇取得者一覧としてGASで毎朝自動投稿する機能を作成しました。

![slack_休暇連絡.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/3e98a004-3a2b-4bc8-addf-fbc900c1d60c.png)

# 通知までの流れ
1. 休暇取得者がSlackのワークフローで情報をフォームに入力
2. 入力した情報がチャンネルへ投稿&スプレッドシートに追記される
3. 毎朝、スプレッドシートからその日の休暇取得者情報を取得
4. チャンネルに自動投稿

# 使用技術
- Slackのワークフロー
- GAS（GoogleAppsScript）
- [incoming webhooks](https://tools.slack.dev/java-slack-sdk/ja-jp/guides/incoming-webhooks/)（外部からSlackにメッセージを投稿するための機能）

# Slackのワークフローの作成
**ワークフローの開始イベント**
- Slack内のリンクから開始

**情報をフォームで収集する**
- 申請者：Slackユーザー
- 休暇開始日：日付
- 休暇終了日：日付
- 予定時刻：短い回答
- 備考：リッチテキスト
- 休暇申請はしたか：チェックボックス
- カレンダーに休暇予定を記入したか：チェックボックス

**チャンネルへメッセージを送信する**
- ワークフローが使用されたチャンネル

**Google Sheets**
- 該当のスプレッドシートを選択

# GASで通知機能を作成する
連日取得（例：金と月に取得）に対応するための**日付分割.gs**と、
Slackに通知するための**通知用.gs**の2つを作成します。

スプレッドシートの「拡張機能」から「Apps Script」を選択します。

# 日付を分割する処理
日付を分割してスプレッドシートに追記するとき、入力した日付とずれる現象が発生したため、タイムゾーン関連のコードをしつこく書いてます。
```日付分割.gs
function onChange(e) {
  const sheet = e.source.getSheetByName("休暇連絡");
  const notificationSheet = e.source.getSheetByName("通知用");

  // 編集されたシートが「休暇連絡」であることを確認
  if (sheet.getName() === "休暇連絡") {
    const lastRow = sheet.getLastRow();
    const newData = sheet.getRange(lastRow, 1, 1, sheet.getLastColumn()).getValues()[0];
    
    // データの取得
    const username = newData[0];
    const startDate = new Date(newData[1]);
    const endDate = new Date(newData[2]);
    const plannedTime = newData[3];
    const remarks = newData[4];

    // タイムゾーンを適用して日付をローカルタイムゾーンに設定
    const timezone = Session.getScriptTimeZone();
    startDate.setHours(0, 0, 0, 0);
    endDate.setHours(0, 0, 0, 0);

    // startDateをローカルタイムゾーンでの正しい日付に調整
    const startDateLocal = Utilities.formatDate(startDate, timezone, "yyyy-MM-dd");

    let settingDate = new Date(startDateLocal); // startDateLocal を元に settingDate を設定
    let rowIndex = notificationSheet.getLastRow() + 1; // 書き込み開始行

    // 休暇日数分、「通知用」シートにデータを追加
    do {
      // settingDate をローカルタイムゾーンに合わせてフォーマット
      const formattedDate = Utilities.formatDate(settingDate, timezone, "yyyy-MM-dd");

      // 都度書き込み
      notificationSheet.getRange(rowIndex, 1, 1, 4).setValues([[formattedDate, username, plannedTime, remarks]]);
      
      // 次の行へ
      rowIndex++;
      
      // 日付を進める
      settingDate.setDate(settingDate.getDate() + 1); // 日付を進める
      settingDate.setHours(0, 0, 0, 0); // 時間部分をリセット
    } while (settingDate <= endDate);
  }
}

```

# Slackに通知する処理
Slackに通知するためにはincoming webhookのURLを設定する必要があります。
(この記事では設定方法は割愛させていただきます）
平日のみ通知されるように`getDayメソッド`を使っています。
```通知用.gs
function sendVacationList() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('通知用');
  var data  = sheet.getDataRange().getValues();
  var today = new Date();
  var month = today.getMonth()+1;
  var date  = today.getDate(); 
  var vacationUsers = [];
  var vacationTimes = [];
  var vacationNotes = [];

  // 今日の休暇取得者を抽出
  for (var i = 1; i < data.length; i++) {
    var vacationDate = new Date(data[i][0]); // 休暇日を1列目から取得
    if (vacationDate.toDateString() == today.toDateString()) {
      vacationUsers.push(data[i][1]); // ユーザー名を2列目から取得
      vacationTimes.push(data[i][2]); // 予定時刻を3列目から取得
      vacationNotes.push(data[i][3]); // 備考を4列目から取得
    }
  }

  // 休暇取得者がいた場合かつ平日であればSlackに送信
  if (today.getDay() === 0 || today.getDay() === 6){ // 0 (日曜日) ～ 6 (土曜日)
    console.log("休日のため通知しない");
  } else {
    if (vacationUsers.length > 0) {
      var message = month + '月' + date + '日の休暇予定の方:\n'
      // ユーザー名とその休暇時間を組み合わせて改行で区切ってリストにする
      for (var i = 0; i < vacationUsers.length; i++) {
      message += vacationUsers[i] + ', ' + vacationTimes[i] + ', ' + vacationNotes[i] + '\n';
      } 
    } else {
      // 休暇取得者がいない場合
      var message = month + '月' + date + '日の休暇予定の方:\n'
      var message = '本日の休暇取得者はいませんでした。';
    }
    
    var webhookUrl = 'Webhook URLをここに入力する';
    var payload = JSON.stringify({text: message});
    var options = { method: 'POST', contentType: 'application/json', payload: payload };

    UrlFetchApp.fetch(webhookUrl, options);
  }
}
```

# まとめ
GASとSlackワークフローで収集した情報を自動投稿する機能を作ることができました。
（知識不足で、var,let,constの使い分けが正しくできていないかも……）

今後は、「朝のお知らせ」としてその日の情報を一覧で通知できるように改良したいです。


完成イメージ
![slack_完成イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/08a77dbd-4647-a3aa-43fa-f0ac7b69f713.png)