GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

2021年3月13日
ツール
Pocket

1日ひとりが担当する掃除当番、これを毎日順番にSlackに通知したいとか思うことないでしょうか。

毎日担当が変わるけど自分の順番を確認するのは忘れがちだしやらなくなってしまう原因になります。

そこで、担当順を書いたスプレッドシートを作成して、GASを使ってSlackに通知する仕組みを実装したいと思います。

前提条件

  • Google スプレッドシートを利用する。
  • 通知処理は、GAS(Google Apps Script)で実装する。
  • Slackへの通知は、プロジェクトキー:1on93YOYfSmV92R5q59NpKmsyWIQD8qnoLYk-gkQBI92C58SPyA2x1-bqのライブラリを利用して行う。
  • GASのトリガー設定が1時間の範囲内での設定の為、毎朝8時ジャストに通知ということはできません。8時〜9時の間のどこかで通知になります。

スプレッドシート、GASの作成

以下のように、担当者名を記述したシートをGoogle スプレッドシートで作成します。

シート名は、担当者にしておきます。ファイル名(担当者お知らせシート)もつけておきます。

A列の利用していない部分にはデータを入力しないようにしてください。(最終行の判断に利用しているため)

最初の通知を担当Aにしたい場合は、担当Dの前回フラグの列に○をつけておきます。スキップしたい人がいる場合は、スキップの列に○をつけておきます。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

この状態で、順番にA=>B=>C=>D=>A=>B・・・・になるようにプログラムを実装します。

以下からApps Scriptを起動します。(拡張機能 => Apps Script)

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法
GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

既にあるmyFunctionを消して以下のコードを貼り付けます。

function myFunction() {
  // 対象のシートを取得します
  const sheet = getTargetSheet();
  if(!sheet) {
    Logger.log('sheet not found');
    return false;
  }

  // A列の最終行を取得します
  const columnVals = sheet.getRange('A:A').getValues();
  const lastRow = columnVals.filter(String).length - 1;

  // シートのヘッダ部分を除くデータを取得します(2行目の1列目から最終行の4列目までを取得)
  // スプレッドシートの処理でループ内で、getValue等すると重くなるため
  const data = sheet.getRange(2, 1, lastRow, 4).getValues();
  let row = 0;
  let prev = false;

  for(let i = 0; i < data.length; i++) {
    // 前回フラグがあり、スキップがない場合は、本日の担当者とする
    if(prev && data[i][3] !== "○") {
      row = i;
      break;
    }
    // 前回フラグが入っている場合
    if(data[i][2] == "○") {
      prev = true;
    }
    // 最終行までいったら先頭に戻す
    if(i == lastRow - 1 && prev) {
      i = -1;
    }
  }

  // 前回フラグをクリアします
  sheet.getRange(2, 3, lastRow, 1).setValue('');
  // フラグをセットする
  sheet.getRange(row+2, 3).setValue('○');

}

function getTargetSheet() {
  let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = spreadsheet.getSheetByName('担当者');
  return sheet;
}

コピペしたら、保存ボタンを押します。(ページの最後に、Bing AIがきれいにしてくれたソースの記載があります)

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

この状態で、プログラムを実行しようとすると以下のような権限の確認が出るのでOKにして進めます。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法
GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

「このアプリはGoogleで確認されていません」と出ます。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

慌てないでください。

詳細をクリックすると以下が出てくるので、安全ではないページに移動をクリックします。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

以下の権限確認が出るので、許可をします。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

プログラムを実行するたびに、前回フラグの”○”が移動していくのが確認できます。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法
GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

あとは、Slackの通知に必要な部分を実装していきます。

Slack通知の実装

まずは、Slack通知用のライブラリを読み込みます。

左ナビのライブラリの+ボタンをクリックするとポップアップが表示されます。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

以下のコードを入力して検索ボタンをクリックします。

1on93YOYfSmV92R5q59NpKmsyWIQD8qnoLYk-gkQBI92C58SPyA2x1-bq

バージョン6の状態で出るので、最新のバージョンに切り替えて、追加ボタンをクリックします。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

以下の関数を追加します。

“ここには、・・・”の部分は、それぞれの環境で取得したもの(トークン、チャンネルID)を入れてください。

Slackのトークンについては、レガシーなものではなく、必要な機能にしかアクセス権を与えないトークンを使うようにしてください。

ここでは、トークンの取得、チャンネルIDについては割愛します。

function sendSlack(slackID) {
  let slackApp = SlackApp.create('ここには、Slackで取得したトークンを設定');
  let channelId = 'ここには、slackのチャンネルIDを設定';
  let message = slackID + '\n本日掃除当番です。';
  slackApp.postMessage(channelId, message);
}

最初に書いたコードの最下部に、上記の関数を呼ぶ処理を追加します。

  // 前回フラグをクリアします
  sheet.getRange(2, 3, lastRow, 1).setValue('');
  // フラグをセットする
  sheet.getRange(row+2, 3).setValue('○');
  // Slackに通知する
  sendSlack(data[row][1]);

シートのslackIDの部分には、各担当者のslackのIDを以下の形式で、記入するようにしてください。

<@UG12345678>

あとは、実行を定期的に行うように設定します。

トリガーの設定

左ナビからトリガーをクリックします。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

トリガーを追加をクリックします。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

イベントのソースを選択を時間主導型にし、日付ベースのタイマー、時間を指定して保存します。

GAS(Google Apps Script)を使って掃除当番を順番でSlackに通知する方法

以上で、指定したチャンネル内で、担当者宛に、通知がされるようになります。

ではでは。

bing AIでのソース解説してもらいました。

// myFunctionという関数を定義します
function myFunction() {
  // 対象のシートを取得します
  const sheet = getTargetSheet(); // getTargetSheetという関数を呼び出して、担当者という名前のシートを取得します
  if(!sheet) { // シートが存在しない場合は
    Logger.log('sheet not found'); // sheet not foundとログに出力します
    return false; // 関数を終了します
  }

  // A列の最終行を取得します
  const columnVals = sheet.getRange('A:A').getValues(); // A列全体の値を配列で取得します
  const lastRow = columnVals.filter(String).length - 1; // 空白でない値だけにフィルターして、その長さから1引いたものが最終行です

  // シートのヘッダ部分を除くデータを取得します(2行目の1列目から最終行の4列目までを取得)
  // スプレッドシートの処理でループ内で、getValue等すると重くなるため
  const data = sheet.getRange(2, 1, lastRow, 4).getValues(); // ヘッダー以外のデータ部分(名前、日付、前回フラグ、スキップ)を配列で取得します
  let row = 0; // 担当者が何行目にいるかを記録する変数です。初期値は0です。
  let prev = false; // 前回フラグが入っているかどうかを記録する変数です。初期値はfalseです。

  for(let i = 0; i < data.length; i++) { // データ部分の行数分だけ繰り返し処理をします。iは現在の行番号(0から始まる)です。
    // 前回フラグがあり、スキップがない場合は、本日の担当者とする
    if(prev && data[i][3] !== "○") { // 前回フラグがtrueであり、かつスキップが"○"ではない場合は(つまり今回担当可能な場合は)
      row = i; // 担当者がi行目にいることになるので、rowにiを代入します。
      break; // 繰り返し処理を終了します。
    }
    // 前回フラグが入っている場合
    if(data[i][2] == "○") { // 前回フラグが"○"になっている場合は(つまり前回担当した人が見つかった場合は)
      prev = true; // prevにtrueを代入しておきます。
    }
    // 最終行までいったら先頭に戻す
    if(i == lastRow - 1 && prev) { // 最終行まで来てしまって、かつprevがtrueになっている場合は(つまり前回担当した人より後ろに今回担当可能な人がいなかった場合は)
      i = -1; // iに-1を代入しておきます。これにより次の繰り返しではi=0から始まって先頭から探すことになります。
    }
  }
 // 前回フラグをクリアします
  sheet.getRange(2, 3, lastRow, 1).setValue(''); // データ部分全体の前回フラグ(C列)に空白文字列'' をセットしてクリア
// フラグをセットする
  sheet.getRange(row+2, 3).setValue('○'); // 担当者の行の前回フラグ(C列)に"○" をセットします。
}

// 担当者という名前のシートを取得する関数を定義します
function getTargetSheet() {
  let spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // 現在開いているスプレッドシートを取得します
  let sheet = spreadsheet.getSheetByName('担当者'); // スプレッドシートから担当者という名前のシートを取得します
  return sheet; // シートを返します
}

bing AIでもっと簡潔にわかりやすくしてもらいました。

// 担当者シートから本日の担当者を決めてフラグをセットする関数
function decideTodayPerson() {
  // 担当者シートを取得します
  const personSheet = getPersonSheet();
  if(!personSheet) { // シートが存在しない場合はエラーにします
    throw new Error('person sheet not found');
  }

  // A列の最終行(空白でない行)を取得します
  const lastRow = personSheet.getRange('A:A').getValues().filter(String).length - 1;

  // シートのヘッダ部分(1行目)を除くデータ部分(名前、日付、前回フラグ、スキップ)を配列で取得します
  const data = personSheet.getRange(2, 1, lastRow, 4).getValues();
  
  // 前回担当した人の次に担当可能な人(スキップがない人)が本日の担当者です。
  
  let todayPersonRow = -1; // 担当者が何行目にいるかを記録する変数です。初期値は-1です。
  
  let prevFlagFound = false; // 前回フラグが入っているかどうかを記録する変数です。初期値はfalseです。
  
  for(let i = 0; i < data.length * 2; i++) { // データ部分の行数分だけ繰り返し処理をします。iは現在の行番号(0から始まる)です。
    let row = data[i % data.length]; // i番目の行データ(配列)を取得します。iがdata.length以上になったら先頭から再び探すように%演算子で余りだけ使います。
    if(prevFlagFound && row[3] !== "○") { // 前回フラグがtrueであり、かつスキップが"○"ではない場合は(つまり今回担当可能な場合は)
      todayPersonRow = i % data.length; // 担当者がi%data.length行目にいることになるので、todayPersonRowにi%data.length を代入します。
      break; // 繰り返し処理を終了します。
    }
    if(row[2] == "○") { // 前回フラグが"○"になっている場合は(つまり前回担当した人が見つかった場合は)
      prevFlagFound = true; // prevFlagFoundにtrueを代入しておきます。
    }
}

  if(todayPersonRow == -1) { // 担当者が見つからなかった場合はエラーにします
    throw new Error('today person not found');
  }

  // 前回フラグをクリアします
  personSheet.getRange(2, 3, lastRow, 1).setValue(''); // データ部分全体の前回フラグ(C列)に空白文字列'' をセットしてクリアします
  
  // フラグをセットする
  personSheet.getRange(todayPersonRow+2, 3).setValue('○'); // 担当者の行の前回フラグ(C列)に"○" をセットします。
}

// 担当者という名前のシートを取得する関数
function getPersonSheet() {
  let spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // 現在開いているスプレッドシートを取得します
  return spreadsheet.getSheetByName('担当者'); // スプレッドシートから担当者という名前のシートを取得して返します
}