🐡

GoogleSpreadsheet + GAS + Slackで日直管理と通知

に公開

やりたいこと

一定期間ごとに何らかの日直をローテーションで担当し、担当者にSlackで通知したい。
日直の内容や期間、メンバー表はGoogleSpreadsheetで管理する。

前提:SlackのIncoming Webhookを使えるようにする

https://api.slack.com/apps からIncoming Webhookを使えるようにアプリを登録する。
https://hooks.slack.com/services/***** のようなWebhook URLが払い出されるので取得しておく。

GoogleSpreadsheetの設定

メンバー表や通知設定をシートで定義する。

membersシート

SlackのIDの名前の対応表。
「朝会司会」や「レポート作成」などの仕事ごとにローテーション表を管理する際には名前で管理しておき、日直が決まったら名前 → SlackIDを引いてメッセージ通知時に使用する。

works

日直を設定したい仕事ごとの定義。
name は仕事の名前。
intervals は通知間隔の日数で、毎日であれば1を、1週間ごとであれば7を指定する。
next は次回の通知日。定期開催ではなく、何らかの調整が発生したら手動で修正する。

朝会司会、レポート作成

worksで定義したnameのシート名でシートを作成する。
シートの2行目以降に日直の候補となる名前を記述する。

GASの設定

拡張機能Apps Script をクリックすると、GASの入力画面になる。

コード

シートの定義を読み込み、日直担当を決め通知を行う。
さらに、次回の通知日や担当者を更新し、シートの定義に反映させる。
トリガーの実行対象は main 関数。

const SLACK_URL = 'https://hooks.slack.com/services/...'

function getRows(sheetName) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName)
  const [header, ...rows] = sheet.getDataRange().getValues()
  
  return rows.map(row => Object.fromEntries(header.map((key, i) => [key, row[i]])))
}

function getNames(sheetName) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName)

  //namesは [[user1], [user2], ...] のような二次元配列
  const [_, ...names]= sheet.getDataRange().getValues()
  return names
}

function rotate(sheetName) {
  const names = getNames(sheetName)
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName)

  // 先頭を末尾に移動
  names.push(names.shift())

  // 元のデータをクリア
  sheet.getRange(2, 1, sheet.getLastRow() - 1).clearContent()

  // 並び替えたデータをシートに反映
  sheet.getRange(2, 1, names.length, 1).setValues(names)
}

function isSameDate(date1, date2) {
  return date1.getFullYear() === date2.getFullYear() &&
    date1.getMonth() === date2.getMonth() &&
    date1.getDate() === date2.getDate()
}

function postSlack(workName, slackId) {
  const text = `今日の ${workName} の担当は <@${slackId}> です`
  const payload = JSON.stringify({text: text})
  const options = {
    method:      'post',
    contentType: 'application/json',
    payload:     payload,
  }
  UrlFetchApp.fetch(SLACK_URL, options)
}

function calculateNextDate(work) {
  const nextDate = work.next
  return new Date(nextDate.setDate(nextDate.getDate() + work.intervals))
}

// 次回通知日を更新する
function updateWorksSheet(work, index) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('works')
  const nextDate = calculateNextDate(work)

  // 行は1から始まり、1行目はヘッダー行なので+2
  // C列なので3を指定
  sheet.getRange(index + 2, 3).setValue(nextDate)
}

function main() {
  const members = getRows('members')
  const works = getRows('works')

  const today = new Date()
  works.forEach((work, index) => {
    // 通知日以外はスキップ
    if ( !isSameDate(work.next, today) ) { return }

    const dayshift = getNames(work.name).flat()[0]
    const member = members.find(member => member.name === dayshift)

    postSlack(work.name, member.id)
    updateWorksSheet(work, index)
    rotate(work.name)
  })
}

トリガー

コードを実行するためのトリガーを設定する。
毎日実行するようにしておき、コード側で実行日が通知日かどうかを判定して通知する。
時刻は通知したい時間に合わせて設定する。

実行結果

ローテーションの順番を変えたいとき

GoogleSpreadsheet上で行を選択してドラッグ&ドロップで順番を入れ替えることができる。
日直担当が次の日に休みで順番を調整したい場合に便利。

Discussion