Slackとgoogleカレンダーで勤怠記録をしよう

# はじめに
新型コロナウイルスの影響で自粛期間が長らく続いていましたが，6月あたりから感染対策を実施した上で各大学，企業でのが行われるようになりましたね．

僕の大学では研究室の入退室時間を管理することが義務付けられました．これは感染者が出た場合に，感染経路及び感染した可能性のある人を特定するためだと思われます．
他の大学や企業でもこのような感染対策を実施しているのではないでしょうか．

特に管理法についての指定はなかったので，googleカレンダーに記入することで入退室時間を記録することになったのですが，研究室全員で使用するアプリはできる限り少ない方が効率的であるため，指導教員の先生からSlackだけで入退室時間を管理できるようにうまいことやってくれないかと依頼されました．

そこでSlackに勤怠管理チャンネルを作り，「おはよう」や，「さようなら」と呟けばgoogleカレンダーに予定が追加されるプログラムを作成しました．

初投稿であることと，GAS経験が少ないことをご容赦ください．

  
ユーザーが行う操作のイメージ
![slack.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379532/d63395f2-5bf1-9885-26c0-0833c9acd131.png)

googleカレンダーに名前，時間，場所が登録される
![カレンダー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379532/d64c2f65-b7c7-3f3e-7ccd-e867843f5847.png)

スプレッドシートにも登録される
![spread.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379532/852b1158-14ef-c8a2-fe24-95dc02005b10.png)



# 用意するものと使ったツール
- チームで共有しているgoogleカレンダー
- 勤怠管理用のSlackのチャンネル
- Outgoing webhook
- Incoming webhook
- Google Apps Script

# Outgoing webhook
トリガーワードが呟かれたら反応して色々するアプリです
[こちら](https://inomoto-lab.slack.com/apps/A0F7VRG6Q-outgoing-webhook?next_id=0)からSlackの勤怠管理用のチャンネルにOutgoing webhookを追加します．
中身はGoogleAppsScriptで書いていきます．
![outgoing.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379532/7251d977-9ea9-87fa-e499-2b5ac3dc4fc3.png)
引き金となる言葉は自由に変更できます．inとoutだけで構いませんが，味気ないので多めに設定しました．
URLは，GoogleAppsScriptのアプリのURLを張り付けるので，この時点では空欄で構いません．
トークンはGoogleAppsScriptで使います．

# Incoming webhook
呟いてレスポンスが無かったらカレンダーに追加されたのか懐疑的になるのと，味気ないので，反応してくれるアプリを追加します．
さきほどと同様に[こちら](https://inomoto-lab.slack.com/apps/A0F7XDUAZ--incoming-webhook-?next_id=0)から勤怠管理用のチャンネルにIncoming webhookを追加します．
![incoming.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379532/d8926b8e-4304-b51e-3009-731149907974.png)
レスポンスしてくれるbotのアイコンはここで設定できます．

# Google Apps Script
googleドライブから，Googleスプレッドシートを新規作成します．
(1, 1)から(1, 6)セルに
カレンダーへの登録,日付,タイトル(人),開始時間,終了時間,場所,
と記入します．スプレッドシートでも勤怠管理を登録していきます．
次にツールタブのスクリプトエディタを開きます．
以下のソースコードをコピペします．

```js:SlackToCalendar.gs
function doPost(e) {
  //シート1はシート名に応じて変更
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1');
  
  // googleカレンダーのID
  const calendar = CalendarApp.getCalendarById("[googleカレンダーの設定からIDをコピペ]")

  //Outgoing Webhookのトークン
  var token = '[Outgoing webhookのトークンをコピペ]'

  //送られてきたトークンが正しければ勤怠を記録
  //parameterは必要に応じて変更
  if (token == e.parameter.token){
    var id_to_name = {"U0*********":"金子","U0*********":"本多","U0*********":"阿部"}
    var datetime     = new Date();
    var date         = (datetime.getFullYear()  + ('0' + (datetime.getMonth() + 1)).slice(-2) + ('0' + datetime.getDate()).slice(-2))
    var user_name    = id_to_name[e.parameter.user_id];
    var trigger_word = e.parameter.trigger_word;
    var text         = e.parameter.text;
　　// 入室に反応する言葉（Outgoing webhookで設定した物）をリストに追加
    var list = ["in","おはよう","出勤","こんにちは"]
    var message
    
    if (list.includes(trigger_word)) {
      // 出勤時のプログラム
      
      // 場所を追加
      text = text.replace("＿","_")
      var mark = "_"
      var index = text.indexOf(mark)
      text = text.slice(index + 1)
      
      
      //追加する配列を作成
      array = ["",date,user_name,datetime,"",text];
      //シートの最下行に配列を記述
      sheet.appendRow(array);
      
      message = Utilities.formatString("%sさんが%sに%sしました",user_name,text,trigger_word)
      
    } else {
      // 退勤時のプログラム
      message = Utilities.formatString("%sさん%s",user_name,trigger_word)
      
      for(var i=2; i<= sheet.getLastRow(); i++) {
        // カレンダーに登録済みならパス
        if (sheet.getRange(i, 1).getValue().toString() == "済") {
          continue;
        }
        // 日付とuser_nameが一致しているものを探す
        var in_date = sheet.getRange(i,2).getValue()
        var in_datetime = sheet.getRange(i,4).getValue()
        var in_name = sheet.getRange(i,3).getValue().toString()
        var in_location = sheet.getRange(i,6).getValue().toString()
        
        // 一致していたらoutの時間を登録，カレンダーにも登録
        if (( in_date == date ) && ( in_name == user_name )) {
          
          sheet.getRange(i,5).setValue(Utilities.formatDate(datetime,"JST", "yyyy/MM/dd HH:mm:ss"))
          var startTime = new Date(in_datetime)
          var endTime = datetime
          var options = {
            location: in_location
          }
          calendar.createEvent(user_name, startTime, endTime, options)
          sheet.getRange(i,1).setValue("済")
          break
          
        }
      }
    }
    var payload = {
      "text" : "\n" + message, // メッセージの本文
      "channel" : "#勤怠管理", // チャネルの指定
      "username" : "勤怠管理クマ", // Botの名前
      }
    postSlack(payload);
  }
  return
}

// Slackでレスポンスする関数
function postSlack(payload)
{
  var options = {
    "method" : "POST",
    "payload" : JSON.stringify(payload)
  }

  var url = "[Incoming webhookのURL]"; // Webhook URL
  var response = UrlFetchApp.fetch(url, options);
  var content = response.getContentText("UTF-8");

}
```

コピペしたら[こちら](https://qiita.com/kunichiko/items/7f64c7c80b44b15371a3)を参考にアプリケーションのURLを取得し，Outgoingq webhookのURL欄に入力します．

急ぎで作ったので仕様として次のような欠陥がありますので，実装する際は注意してください．

- 入室記録を忘れると退室記録をしても登録されない
- 0時をまたぐ入退室記録に対応していない
- Googleスプレッドシートの日付，開始時間，終了時間の見た目が頭悪そう

場所を指定するのに普通は@を使いますが，slackは"@"を入力するとメンションが入力されてしまい煩わしいので"_"を採用しました．
messageは所属するチームの雰囲気に合わせて調整してください．
user_idは，Slackのプロフィール欄からコピペできます．大人数のチームだと大変ですが，Outgoing webhookの仕様で名前を取得できないため，ひと手間かかります．

# おわりに
最後まで読んでいただきありがとうございました．
質問やコメントお待ちしております．

以下のページを参考にしました．
[Slackで簡単に勤怠管理！【GAS】](https://qiita.com/tawasi12/items/1c6a2b66a84401e52c01)