【スプレッドシート × GAS × Slack】誕生日お祝いBotの実装編|Offers Tech Blog

2022/06/30に公開

概要

こんにちは、Offers を運営している株式会社 overflow のバックエンドエンジニアの shun です。
今回は、スプレッドシート × GAS(Google Apps Script) × Slackを用いて、社内活性化に繋がる誕生日お祝い Bot を実装してみます。
弊社でもすでに導入済みで、最近幸いにもメンバーが増えてきたことにより、ほぼ毎日誕生日お祝い Bot が仕事してて嬉しいです笑
フルリモートな企業だからこそ、このような小さいことでも社内活性化につながりますね。

誕生日 Bot の設計

設計は単純で、「毎日誕生日リストを見てお祝いコメントを送る素敵な人」 を自動化するだけです。(権限的に総務部の方だと思うが)
では 「毎日誕生日リストを見てお祝いコメントを送る素敵な人」 はどのように作業するか、手順を洗い出します。

  1. 全社員の誕生日が閲覧できるリストを見る(多分朝一の 9:00 とか)
  2. 今日誕生日の人を検索する
  3. 該当した社員名を集める
  4. Slack にお祝いメッセージ投稿

書き出してて思いましたが、割と 10 分くらい時間取られそうな作業ですね。しかも自分が体調不良とか有給取る際に他の方へお願いする手間や作業自体忘れてしまう可能性もあります。
こういった点でも、やはり単純作業の自動化は魅力的ですね。

前置きが長くなりましたが、具体的なシステム設計していきます。私は以下のように行いました。

  1. 全社員の誕生日が閲覧できるリストを用意する(スプレッドシート)
  2. GAS にて、全社員の誕生日が閲覧できるリストの誕生日列が本日になっている社員を取得
  3. GAS にて、Slack 投稿用のメッセージを作成
  4. GAS にて、Slack へ投稿
  5. 毎日 9:00 に定期実行させる

では実装開始!

スプレッドシートにて、社員リストの準備

ダミーとして 4 名のシートを用意しました。シート名は 「社員リスト」 とします。

スプレッドシートで列名を左から順にID、名前、SlackID、誕生日としてダミーデータが4行分入っているスクショ

GAS にて、本日誕生日の社員データを取得

BirthDay.gs
const celebration = () => {
  const book = SpreadsheetApp.getActiveSpreadsheet()
  const sheet = book.getSheetByName("社員リスト")
  const sheetData = sheet.getDataRange().getValues()
  const header = sheetData[0]

  const today = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'MM/dd')

  const birthDayMemberNames = sheetData.filter(data => {
    return data[header.indexOf('誕生日')] !== '' && Utilities.formatDate(new Date(data[header.indexOf('誕生日')]), 'Asia/Tokyo', 'MM/dd') === today
  }).map(data => {
    return `${data[header.indexOf('名前')]}さん`
  })

  console.log(birthDayMemberNames)
}

// 本日が9/9だと仮定
実行結果 #> ['hoge丸', 'おじゃる丸']

GAS にて、Slack 投稿用のメッセージを作成

BirthDay.gs
const celebration = () => {
  const book = SpreadsheetApp.getActiveSpreadsheet()
  const sheet = book.getSheetByName("社員リスト")
  const sheetData = sheet.getDataRange().getValues()
  const header = sheetData[0]

  const today = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'MM/dd')

  const birthDayMemberNames = sheetData.filter(data => {
    return data[header.indexOf('誕生日')] !== '' && Utilities.formatDate(new Date(data[header.indexOf('誕生日')]), 'Asia/Tokyo', 'MM/dd') === today
  }).map(data => {
    return `${data[header.indexOf('名前')]}さん`
  })

  // ★ 以下追加
  if(birthDayMemberNames.length === 0) return // 誕生日の人がいる場合だけ処理を継続させる

  const message = []
  message.push(`今日は、:birthday: ${birthDayMemberNames.join(', ')}:birthday: のお誕生日です!:clap:`)
  message.push('')
  message.push('おめでとうございます!良い一年を!!:smile:')

  console.log(message.join("\n"))
}

実行結果 #>
今日は、:birthday: hoge丸, おじゃる丸:birthday: のお誕生日です!:clap:

おめでとうございます!良い一年を!!:smile:

GAS にて、Slack へ投稿

GAS から Slack へ投稿するためには incoming Webhook が必要となります。
設定には Slack 公式ヘルプページ をご参照いただくのが一番早いと思います。(頻繁な仕様の変更などが懸念されるため)

https://hooks.slack.com/services/XXXXXXXX/YYYYYYYY のような Webhook URL が取得できたら、以下のように GAS のコードを追記します。

BirthDay.gs
const celebration = () => {
  const book = SpreadsheetApp.getActiveSpreadsheet()
  const sheet = book.getSheetByName("社員リスト")
  const sheetData = sheet.getDataRange().getValues()
  const header = sheetData[0]

  const today = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'MM/dd')

  const birthDayMemberNames = sheetData.filter(data => {
    return data[header.indexOf('誕生日')] !== '' && Utilities.formatDate(new Date(data[header.indexOf('誕生日')]), 'Asia/Tokyo', 'MM/dd') === today
  }).map(data => {
    return `${data[header.indexOf('名前')]}さん`
  })

  if(birthDayMemberNames.length === 0) return // 誕生日の人がいる場合だけ処理を継続させる

  const message = []
  message.push(`今日は、:birthday: ${birthDayMemberNames.join(', ')}:birthday: のお誕生日です!:clap:`)
  message.push('')
  message.push('おめでとうございます!良い一年を!!:smile:')

  // ★ 以下追加
  notificationToSlack(message)
}

// ★ 以下追加
const notificationToSlack = (message) => {
  const postUrl = "https://hooks.slack.com/services/XXXXXXX/YYYYYYYYY"
  const jsonData = { "text": message }
  const payload = JSON.stringify(jsonData)
  const options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  }
  UrlFetchApp.fetch(postUrl, options)
}

これで、celebration メソッドを実行すれば本日誕生日の方をまとめて Slack にてお祝いする Bot 投稿が完成し、結果として以下のような投稿が Slack に POST されます。

誕生日BotによるSlack投稿結果のスクショ

最後に、この処理を毎日実行させるために定期実行設定を行います。

定期実行の設定

トリガーと呼ばれる定期実行ジョブの設定・管理機能があります。超便利です。

GAS の左サイドバー > トリガー を選択

GASエディタからトリガー画面への導線を示しているスクショ

トリガー画面から、トリガーの追加

画面右下のトリガーを追加ボタンをクリックするとモーダルが開きます。

新規トリガー追加時に表示されるモーダルのスクショ

今回は 毎日 9:00 に celebration メソッドが実行させる という設定をします。
以下のように情報を入力しましょう。

  • 実行する関数を選択: celebration
  • 実行するデプロイを選択:Head
  • イベントのソースを選択:時間手動型
  • 時間ベースのトリガーのタイプを選択:日付ベースのタイマー
  • 時刻を選択:午前 9 時〜10 時

これで保存ボタンを押せば、無事に誕生日 Bot の実装完了です!!

導入してみてよかった点

社内のコミュニケーションが生まれやすい仕組みづくりに少しは貢献できたかなと実感してます。
そして、意外と同じ誕生日のメンバーがおり、盛り上がっていて嬉しいです w
調べてみたところ、およそ 40 名規模のメンバー内に同じ誕生日の人がいる確率は 89%程あるようですね。
私の誕生日はまだこの Bot にお祝いされたことはありませんが、再来月なので楽しみにしております w

付録

GAS のトリガーにて、厳密に定期実行時間を指定したい場合

トリガーも GAS の 1 機能のため、GAS からトリガー設定できてしまうんです。便利。
例として、毎日 9:00 に celebration メソッドが実行されるように設定してみましょう。

CustomTriggers.gs
const setTriggerForCelebration = () => {
  let setTime = new Date()
  setTime.setDate(setTime.getDate())
  setTime.setHours(9)
  setTime.setMinutes(00)
  ScriptApp.newTrigger('celebration').timeBased().at(setTime).create()
}

このメソッドを叩くことで、本日 9:00 に celebration メソッドを実行するトリガーの設定が完了します。

ちょっとしたテクニックですが、この setTriggerForCelebration メソッド自体を本日 2:00〜3:00でトリガー設定すれば、毎日 9:00 に celebration メソッドが実行されます。

まとめ

今回は GAS(Google Apps Script)を用いて、スプレッドシートの情報から Slack へ投稿する誕生日 Bot の実装方法についてまとめました。必ずしもこのようなプログラムを書く際に、エンジニアでなければならない必要はないと考えております。ただし、気づいた人がどんどんやっていくということを受け入れてくれる社風が大事だなぁと最近実感しております。

少々長くなりましたが、最後まで読んで頂き、ありがとうございました。「いいね」していただけると記事執筆の励みになりますので、参考になったと思った方は是非よろしくお願いします!

次回は Slack App の実装方法について書きたいと思います。

関連記事

https://zenn.dev/offers/articles/20220616-google-app-script-technique

Offers Tech Blog

Discussion