素人が翌日の勤務(シフト)を告げるLINEbotを作った話【13】

前回はフォームの中身を作りました。今回はフォームの回答があったときの動作について書いていきたいと思います。

まずは1回試してみます。

画像1

"たなか"の12/7の勤務は元々日勤ですが、これを遅番に変更したいと思います。

画像2

送信すると、スプレッドシートに回答した内容が反映されます。ここから値を取得して、勤務表に変更を加えていきます。

それではさっそくコードを書きたいのですが、今回のコードは今までとは異なり、フォーム上のスクリプトエディタに書きます。

画像3

右上の点が縦に3つ並んでいるところから選べます。スクリプトエディタを選択すると、久々に見るまっさらなエディタ画面が現れます。筆者はgasを始めた当初、コードが消えてしまったのかと思って大パニックとなりましたが、皆様はご安心を。スプレッドシートに紐づいたエディタとフォーム紐づきのエディタは別物です。スプレッドシート側に書けそうなのに、なぜフォーム側に書くのかについては後述します。

画像4

"myFunction"を消し、以下のコードをペーストします。

var channel_access_token = "トークン*******************";
var headers = {
  "Content-Type": "application/json; charset=UTF-8",
  "Authorization": "Bearer " + channel_access_token
};
var spreadsheet = SpreadsheetApp.openById("ID**********************");
var sheet = spreadsheet.getSheetByName("勤務表");
var userSheet = spreadsheet.getSheetByName("ユーザー");
var formSheet = spreadsheet.getSheetByName("勤務変更");

//勤務変更
function changeWorkinglistByForm(){
 
 const dateRow = formSheet.getRange(2,4,formSheet.getLastRow());
 dateRow.setNumberFormat('MM/dd');
 const userData = formSheet.getRange(formSheet.getLastRow(),2).getValue();
 const nameCell = formSheet.getRange(formSheet.getLastRow(),3)
 if(nameCell.isBlank()){
   var nameData = userData
   }else{
     var nameData = nameCell.getValue();
   };
 
 const dateData = formSheet.getRange(formSheet.getLastRow(),4).getValue();
 
 const month = ("00" + (dateData.getMonth()+1)).slice(-2);
 const day = ("00" + (dateData.getDate())).slice(-2);
 const date = month + "/" + day
 
 const nameFinder = sheet.createTextFinder(nameData).findAll();
 const dateFinder = sheet.createTextFinder(date).findAll();
 
 for ( var i = 0; i < nameFinder.length; i++ ) {
       var nameRow = nameFinder[i].getRow();
   };
 for ( var i = 0; i < dateFinder.length; i++ ) {
       var dateColumn = dateFinder[i].getColumn();
   };
 const newshift = formSheet.getRange(formSheet.getLastRow(),5).getValue();
 const oldShift = sheet.getRange(nameRow,dateColumn).getValue();
 sheet.getRange(nameRow,dateColumn).setValue(newshift);
 
 const text = "【勤務変更】\n日付:" + date + "\n職員:" + nameData + "\n変更内容:" + oldShift + "→"+ newshift +"\nby:" + userData ;
 
 sendLineMessageUsingBroadcast(text);
 
 const user =userSheet.getRange(2,4,userSheet.getLastRow());
 const userNumber = user.getValues().length-1;
 const userFinder = user.createTextFinder(userData).findAll();
 for ( var i = 0; i < userFinder.length; i++ ) {
       var userId = userFinder[i].offset( 0 , -1).getValue();
  }
 
 const textToUser = "変更内容が登録者全員に通知されました。\n登録者は現在"+ userNumber + "人です。従業員全員に伝わっているわけではありませんのでご注意ください"
 sendLineMessageFromUserId(textToUser,userId);
};

//管理者にメッセージを送る
function sendLineMessageFromUserId(text,userId) {
var url = "https://api.line.me/v2/bot/message/push";
var postData = {
  "to": userId,
  "messages": [{
    "type": "text",
    "text":  text,
  }]
};
var options = {
  "method": "POST",
  "headers": headers,
  "payload": JSON.stringify(postData)
};
return UrlFetchApp.fetch(url, options);
}

//登録者全員にメッセージを送る
function sendLineMessageUsingBroadcast(text) {
var Url = "https://api.line.me/v2/bot/message/broadcast";
var postData = {
    "messages": [{
    "type": "text",
    "text":  text,
  }
              ]};
var options = {
  "method": "POST",
  "headers": headers,
  "payload": JSON.stringify(postData)
};
return UrlFetchApp.fetch(Url, options);
}

長いですが、実は記述した3つの関数のうち2つは既出です。LINEメッセージを送信する”sendLineMessageFromUserId”と”sendLineMessageUsingBroadcast”が再び登場しています。スクリプトエディタが2つに分かれたのでアクセストークンやスプレッドシートのID、LINEの送信関数など、もう一度書かないといけないのです。

changeWorkinglistByForm

それでは関数”changeWorkinglistByForm”の内容を解説します。

 const dateRow = formSheet.getRange(2,4,formSheet.getLastRow());
 dateRow.setNumberFormat('MM/dd');

最初の2行では、回答シート(”勤務変更”)に返った日付の表示形式を勤務表と合わせる(MM/dd形式)コードを書いています。

  const userData = formSheet.getRange(formSheet.getLastRow(),2).getValue();
  const nameCell = formSheet.getRange(formSheet.getLastRow(),3)
  if(nameCell.isBlank()){
    var nameData = userData
    }else{
      var nameData = nameCell.getValue();
    };

つぎに、回答者(user)とシフト変更の対象者(name)の処理です。フォーム上では、回答者と変更の対象者が同一の場合、対象者の入力を省略しても良い、という仕様にしていました。ここはその部分の処理で、口語で書くとif(もし)nameCell.isBlank(対象者の回答セルが空欄なら)→nameData=userData(対象者と回答者は同じ)、else(ほかの場合、つまり空欄でないなら)→nameCell.getValue(回答セルの内容が対象者)、ということになります。口語にするとかえってわかりづらいですね。

  const dateData = formSheet.getRange(formSheet.getLastRow(),4).getValue();
  const month = ("00" + (dateData.getMonth()+1)).slice(-2);
  const day = ("00" + (dateData.getDate())).slice(-2);
  const date = month + "/" + day

ここはこの後日付を勤務表上で検索して一致させるための処理ですが、今見るとgetDisplayValue使えば1行で書けたんじゃないかと。欲しい値を表示させるためにとんでもなく遠回りしたコードを書いてしまうのは素人の定めですね。これはこれで努力の痕跡として残しておきます(笑)

 const nameFinder = sheet.createTextFinder(nameData).findAll();
 const dateFinder = sheet.createTextFinder(date).findAll();
 
 for ( var i = 0; i < nameFinder.length; i++ ) {
       var nameRow = nameFinder[i].getRow();
   };
 for ( var i = 0; i < dateFinder.length; i++ ) {
       var dateColumn = dateFinder[i].getColumn();
   };
 const newshift = formSheet.getRange(formSheet.getLastRow(),5).getValue();
 const oldShift = sheet.getRange(nameRow,dateColumn).getValue();
 sheet.getRange(nameRow,dateColumn).setValue(newshift);

続いて、頻繁に使っているcreateTextFinderとfor文で日付と変更対象者の行列を取得し、その交わる部分のセルに新しいシフトを書きます。今回は12/7の”たなか”の勤務を変更するので、F2セルが新しいシフトに変更されます。

 const text = "【勤務変更】\n日付:" + date + "\n職員:" + nameData + "\n変更内容:" + oldShift + "→"+ newshift +"\nby:" + userData ;
 
 sendLineMessageUsingBroadcast(text);
 
 const user =userSheet.getRange(2,4,userSheet.getLastRow());
 const userNumber = user.getValues().length-1;
 const userFinder = user.createTextFinder(userData).findAll();
 for ( var i = 0; i < userFinder.length; i++ ) {
       var userId = userFinder[i].offset( 0 , -1).getValue();
  }
 
 const textToUser = "変更内容が登録者全員に通知されました。\n登録者は現在"+ userNumber + "人です。従業員全員に伝わっているわけではありませんのでご注意ください"
 sendLineMessageFromUserId(textToUser,userId);
};

最後に、変更内容をテキストとして成形し、登録者全員に通知する関数”sendLineMessageUsingBroadcast”で送信します。

そこから下の部分はおまけなのですが、このツールはあくまで公式のものではないので、職場内の全員が登録しているわけではありません。なので、ここで通知したことは登録者しか知らないわけですから、必要な申し送りは自分でしてね、というメッセージを、回答者に送る処理を書いています。ちなみに、LINEのメッセージとは異なりGoogleフォームで回答しても回答者を特定してメッセージを送ることはできません。どのように実現しているかというと、フォームの設問①で選んでもらった回答者の名前をユーザーリストで検索し、ユーザーIDから送信しているのです。なので、Aさんが設問①を誤ってBさんを選択してしまうと、BさんのLINEに「勤務変更通知しておいたから!ちゃんと申し送りはしてね!」という謎のメッセージが送られる、という欠陥を抱えていることはお伝えしておきます。もしいらなければここは削除してください。

トリガー設定

最初の方に、なぜフォーム側のエディタで書くのか、という話がでました。それは、フォーム側のスクリプトは「フォーム送信時トリガー」が使えるためです。フォーム送信時トリガーとは、その名前の通りフォームに回答があった際にトリガーが発動するというものです。

画像5

これを設定すると、フォームの回答と同時に勤務表のシフト変更→メッセージ送信まで自動で実行されます。

テストしてみます

今回は、12/7に遅番にした”たなか”のシフトを日勤に戻してみます。設問②は省略してみました。

画像6

トリガーが発動し、changeWorkinglistByFormが動いたようです。12/07の勤務が遅から日に戻っています。

画像7

そして、LINEの画面にはメッセージが届きました。2通届いていて、上の変更内容は登録者全員に、2つ目は回答者に返されるしくみです。

画像8

登録者が一人だと”従業員全員”どころか”誰にも伝わっていません”と書くべきかもしれませんね。うん、悲しい。

こんなところで、今日はおしまいです。次回からはGoogleカレンダーとの連携について書きます。

この記事が気に入ったらサポートをしてみませんか?