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

関数を2つ書きます

前回は、メッセージの送り方をいくつかご紹介しました。今回はいよいよメッセージの中身を書いていきます。今回書く関数は2つ。

1つめ”getWorkDataFromSheet”は、スプレッドシートの勤務表から特定の日付の勤務データを取得して、LINEで流れたときに見やすい文字列に成形する関数

2つ目"sendTomorrowsShiftEverynight"は、”getWorkDataFromSheet”からデータを受け取って、前回書いた"sendLineMessageUsingBroadcast"を使って全員にメッセージを送る関数です。

それでは実際にかいていきます。

”getWorkDataFromSheet”

正直に言うと、ここのコードには自信がないのです。長ったらしくて、もっとキレイに短く書けるんじゃないかと思うのですが、うまくいかず。まあやりたいことが実現できているのでいいのですが…。もしこんな書き方があるよ!という方がいらっしゃったら是非とも教えていただきたいものです。

function getWorkDataFromSheet(targetDate){ 
 const week = ["日","月","火","水","木","金","土"];
 const lastRow = sheet.getLastRow();
 const month = Utilities.formatDate(targetDate, 'JST', 'MM');
 const day = Utilities.formatDate(targetDate, 'JST', 'dd');
 const date = month + "/" + day 
 const dateFinder = sheet.createTextFinder(date).findAll();
 
   for ( var i = 0; i < dateFinder.length; i++ ) {
       var dateColumn = dateFinder[i].getColumn();
   }
 
 const data = sheet.getRange(1,dateColumn,lastRow);
 let earlyTurnList = [];
 let dayShiftList = [];
 let lateTurnList =[];
 let nightShiftList = [];
 let afterNightShiftList = [];
 let holidayList = [];
 let paidVacationList = [];
 
 
 let earlyTurnFinder = data.createTextFinder('早').findAll();
 for ( let i = 0; i < earlyTurnFinder.length; i++ ) {
   let earlyTurn = earlyTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   earlyTurnList.push(earlyTurn)
  }
 
 let dayShiftFinder = data.createTextFinder('日').findAll();
 for ( let i = 0; i < dayShiftFinder.length; i++ ) {
   let dayShift = dayShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   dayShiftList.push(dayShift)
  }  
 
 let lateTurnFinder = data.createTextFinder('遅').findAll();
 for ( let i = 0; i < lateTurnFinder.length; i++ ) {
   let lateTurn = lateTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   lateTurnList.push(lateTurn)
  } 
 
 let nightShiftFinder = data.createTextFinder('夜').findAll();
 for ( let i = 0; i < nightShiftFinder.length; i++ ) {
   let nightShift = nightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   nightShiftList.push(nightShift)
  }
 
 let afterNightShiftFinder = data.createTextFinder('明').findAll();
 for ( let i = 0; i < afterNightShiftFinder.length; i++ ) {
   let afterNightShift = afterNightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   afterNightShiftList.push(afterNightShift)
  }
 
 let holidayFinder = data.createTextFinder('公').findAll();
 for ( let i = 0; i < holidayFinder.length; i++ ) {
   let holiday = holidayFinder[i].offset( 0 , - dateColumn + 1).getValue();
   holidayList.push(holiday)
  }
 
 let paidVacationFinder = data.createTextFinder('有').findAll();
 for ( let i = 0; i < paidVacationFinder.length; i++ ) {
   let paidVacation = paidVacationFinder[i].offset( 0 , - dateColumn + 1).getValue();
   paidVacationList.push(paidVacation)
  }

 var text = date +"("+week[targetDate.getDay()]+") 勤務者一覧"+"\n\n" +
     "早番 : " + earlyTurnList +"\n\n" +
     "日勤 : " + dayShiftList +"\n\n"  +
     "遅番 : " + lateTurnList + "\n\n" +
     "夜勤 : " + nightShiftList +"\n\n"+
     "明け : " + afterNightShiftList +"\n\n";
 
 
 if(holidayList.length + paidVacationList.length > 0){
  text = text +  "公・休・有 : \n" + holidayList+ "," + paidVacationList + "\n\n" ;
  }
    
 return text;
}

 簡単にここでやっていることを説明しますと、

①特定の日付がどこの列にあるか探す

②①の列から勤務帯別に勤務者の配列を作る

③②でできた配列をテキストに成形する

以上の3点です。もう少し解説します。

①特定の日付を検索

function getWorkDataFromSheet(targetDate){ 
 const week = ["日","月","火","水","木","金","土"];
 const lastRow = sheet.getLastRow();
 const month = Utilities.formatDate(targetDate, 'JST', 'MM');
 const day = Utilities.formatDate(targetDate, 'JST', 'dd');
 const date = month + "/" + day 
 const dateFinder = sheet.createTextFinder(date).findAll();
 
   for ( var i = 0; i < dateFinder.length; i++ ) {
       var dateColumn = dateFinder[i].getColumn();
   }
 
 const data = sheet.getRange(1,dateColumn,lastRow);

1行目、getWorkDataFromSheetの隣に(targetDate)と書いてあると思います。この部分を引数と言います。この関数を起動するときに、日付のデータを引数として渡してあげて、その日付がスプレッドシート上のどこの列にあるのかをcreateTextFinderとgetColumnを使って検索しています。

今回は、翌日の勤務を確認したいわけですから、例えば本日が11/01だとすると、11/02の日付データを渡してあげるわけですね。11/02のデータがある列は、

無題

ここですね。ここが今回収集するデータだと特定できるわけです。この部分が一番下の"data"という変数に入ります。

②勤務者の配列を作る

 let earlyTurnList = [];
 let dayShiftList = [];
 let lateTurnList =[];
 let nightShiftList = [];
 let afterNightShiftList = [];
 let holidayList = [];
 let paidVacationList = [];
 
 
 let earlyTurnFinder = data.createTextFinder('早').findAll();
 for ( let i = 0; i < earlyTurnFinder.length; i++ ) {
   let earlyTurn = earlyTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   earlyTurnList.push(earlyTurn)
  }
 
 let dayShiftFinder = data.createTextFinder('日').findAll();
 for ( let i = 0; i < dayShiftFinder.length; i++ ) {
   let dayShift = dayShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   dayShiftList.push(dayShift)
  }  
 
 let lateTurnFinder = data.createTextFinder('遅').findAll();
 for ( let i = 0; i < lateTurnFinder.length; i++ ) {
   let lateTurn = lateTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   lateTurnList.push(lateTurn)
  } 
 
 let nightShiftFinder = data.createTextFinder('夜').findAll();
 for ( let i = 0; i < nightShiftFinder.length; i++ ) {
   let nightShift = nightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   nightShiftList.push(nightShift)
  }
 
 let afterNightShiftFinder = data.createTextFinder('明').findAll();
 for ( let i = 0; i < afterNightShiftFinder.length; i++ ) {
   let afterNightShift = afterNightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   afterNightShiftList.push(afterNightShift)
  }
 
 let holidayFinder = data.createTextFinder('公').findAll();
 for ( let i = 0; i < holidayFinder.length; i++ ) {
   let holiday = holidayFinder[i].offset( 0 , - dateColumn + 1).getValue();
   holidayList.push(holiday)
  }
 
 let paidVacationFinder = data.createTextFinder('有').findAll();
 for ( let i = 0; i < paidVacationFinder.length; i++ ) {
   let paidVacation = paidVacationFinder[i].offset( 0 , - dateColumn + 1).getValue();
   paidVacationList.push(paidVacation)
  }

 11/02の例を見ると、早番が1名、日勤が2名、遅番が1名、夜勤1名、夜勤明け1名がいることがわかります。この人数なのでパッと見て数えられますが、実際にはもっと人数が多いわけです。やはり人間がやるべき作業ではありませんねこれは。

 ここでまたcreateTextFinderが登場します。11/02のデータの範囲で、"早"と表示されている場所(F6)を検索し、その場所からoffsetで移動してA列の名前(あべ)を取得します。そして、取得した名前を最初に宣言したearlyTurnListという配列にpushして入れてあげます。

 この動作を、勤務帯毎に行なっているために、長いコードになっちゃってるわけです。どこか省略できないのかな、といつも思っています。

③テキスト成形

var text = date +"("+week[targetDate.getDay()]+") 勤務者一覧"+"\n\n" +
     "早番 : " + earlyTurnList +"\n\n" +
     "日勤 : " + dayShiftList +"\n\n"  +
     "遅番 : " + lateTurnList + "\n\n" +
     "夜勤 : " + nightShiftList +"\n\n"+
     "明け : " + afterNightShiftList +"\n\n";
 
 
 if(holidayList.length + paidVacationList.length > 0){
  text = text +  "公・有 : \n" + holidayList+ "," + paidVacationList + "\n\n" ;
  }
    
 return text;

 最後に、配列に入れられたデータをまとめて見やすく成形してあげます。

”sendTomorrowsShiftEverynight”

function sendTomorrowsShiftEverynight(){
 var today = new Date();
 today.setDate(today.getDate()+1)
 const text = getWorkDataFromSheet(today)
 
 
 sendLineMessageUsingBroadcast(text);
}

 はい、これだけです!

 1~2行目で翌日の日付データを生成し、そのデータを先ほど書いた関数”getWorkDataFromSheet”に渡しています。関数"getWorkDataFromSheet(today)"を実行すると、抽出されたデータが戻り値として返ってくるので、それを関数”sendLineMessageUsingBroadcast”でLINEに送る、というコードです。これは単純ですね。

テストしてみましょう

 今回は11/02のシフトを例にしていますが、試すときはスプレッドシート上の日付を確認してください。検索する日付がシート上に無いとエラーとなりますので気をつけてください。

無題

 スクリプトエディタの上部の「関数を選択」から”sendTomorrowsShiftEverynight”を選択し、②の部分の再生ボタンを押してください。

画像3

 スプレッドシートと見比べて合っていればOKです。

 今の時点では登録者はあなた一人かと思いますが、登録者が増えれば、同じ内容が登録者全員に送られます。

とりあえず今回はここまで。次回はトリガー設定とコードを少し工夫します。

ここまでのコードまとめ

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


function doPost(e) {
 const webhookData = JSON.parse(e.postData.contents).events[0];
 const eventType = webhookData.type;
 const replyToken = webhookData.replyToken;

 switch(eventType){
   case "follow":  //友だち追加
     follow(webhookData);
     break;
   case "message":  //メッセージ受信
     messageEvent(webhookData);
     break;
   case "unfollow":  //ブロック
     unfollow(webhookData)
     break;
   
 }
}

function follow(webhookData){
 const userId = webhookData.source.userId;
 const nickname = getUserProfile(userId);
 const last_row = userSheet.getLastRow();

 userSheet.getRange(last_row+1,2).setValue(nickname);
 userSheet.getRange(last_row+1,3).setValue(userId);
 userSheet.getDataRange().removeDuplicates([3])  
}


function messageEvent(webhookData){
 const userId = webhookData.source.userId;
 const nickname = getUserProfile(userId);
 const timestamp = new Date(webhookData.timestamp)
 const message = webhookData.message.text.split("\n");
 const replyToken = webhookData.replyToken;
 const whatToDo = message[0];
 const name = message[1];
 const date = message[2];
 const newshift = message[3];
 const last_row = userSheet.getLastRow();
 const lastRow = logSheet.getLastRow();

 logSheet.getRange(lastRow+1,1).setValue(timestamp);
 logSheet.getRange(lastRow+1,2).setValue(nickname);
 logSheet.getRange(lastRow+1,3).setValue(userId);
 logSheet.getRange(lastRow+1,4).setValue(message[0]);
 logSheet.getRange(lastRow+1,5).setValue(message[1]);
 logSheet.getRange(lastRow+1,6).setValue(message[2]);
 logSheet.getRange(lastRow+1,7).setValue(message[3]);

 switch (whatToDo) {
    case 'こんにちは':
      sendLineMessageFromReplyToken(replyToken, "さようなら");
      break;
    default:
      return;
      break;  
}
}

function unfollow(webhookData){
 const data = userSheet.getDataRange()
 const userId = webhookData.source.userId;
 const userFinder = data.createTextFinder(userId).findAll();
 for ( var i = 0; i < userFinder.length; i++ ) {
       var userRow = userFinder[i].getRow();
       var user = userFinder[i].offset(0,1).getValue()
   userSheet.deleteRows(userRow);
 }
}

function getUserProfile(userId){ 
 const Url = 'https://api.line.me/v2/bot/profile/' + userId;
 const userProfile = UrlFetchApp.fetch(Url,{
   'headers': {
     'Authorization' :  'Bearer ' + channel_access_token,
   },
 })
 return JSON.parse(userProfile).displayName;
}

function sendLineMessageFromReplyToken(token, replyText) {
var url = "https://api.line.me/v2/bot/message/reply";
var headers = {
 "Content-Type": "application/json; charset=UTF-8",
 "Authorization": "Bearer " + channel_access_token
};
var postData = {
 "replyToken": token,
 "messages": [{
   "type": "text",
   "text": replyText
 }]
};
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);
}

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 sendLineMessageToAdmin(text) {
var url = "https://api.line.me/v2/bot/message/push";
var postData = {
 "to": "ユーザーID",
 "messages": [{
   "type": "text",
   "text":  text,
 }]
};
var options = {
 "method": "POST",
 "headers": headers,
 "payload": JSON.stringify(postData)
};
return UrlFetchApp.fetch(url, options);
}

function sendTomorrowsShiftEverynight(){
 var today = new Date();
 today.setDate(today.getDate()+1)
 const text = getWorkDataFromSheet(today)
 
 
 sendLineMessageUsingBroadcast(text);
}

function getWorkDataFromSheet(targetDate){ 
 const week = ["日","月","火","水","木","金","土"];
 const lastRow = sheet.getLastRow();
 const month = Utilities.formatDate(targetDate, 'JST', 'MM');
 const day = Utilities.formatDate(targetDate, 'JST', 'dd');
 const date = month + "/" + day 
 const dateFinder = sheet.createTextFinder(date).findAll();
 
   for ( var i = 0; i < dateFinder.length; i++ ) {
       var dateColumn = dateFinder[i].getColumn();
   }
 
 const data = sheet.getRange(1,dateColumn,lastRow);
 let earlyTurnList = [];
 let dayShiftList = [];
 let lateTurnList =[];
 let nightShiftList = [];
 let afterNightShiftList = [];
 let holidayList = [];
 let paidVacationList = [];
 
 
 let earlyTurnFinder = data.createTextFinder('早').findAll();
 for ( let i = 0; i < earlyTurnFinder.length; i++ ) {
   let earlyTurn = earlyTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   earlyTurnList.push(earlyTurn)
  }
 
 let dayShiftFinder = data.createTextFinder('日').findAll();
 for ( let i = 0; i < dayShiftFinder.length; i++ ) {
   let dayShift = dayShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   dayShiftList.push(dayShift)
  }  
 
 let lateTurnFinder = data.createTextFinder('遅').findAll();
 for ( let i = 0; i < lateTurnFinder.length; i++ ) {
   let lateTurn = lateTurnFinder[i].offset( 0 , - dateColumn + 1).getValue();
   lateTurnList.push(lateTurn)
  } 
 
 let nightShiftFinder = data.createTextFinder('夜').findAll();
 for ( let i = 0; i < nightShiftFinder.length; i++ ) {
   let nightShift = nightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   nightShiftList.push(nightShift)
  }
 
 let afterNightShiftFinder = data.createTextFinder('明').findAll();
 for ( let i = 0; i < afterNightShiftFinder.length; i++ ) {
   let afterNightShift = afterNightShiftFinder[i].offset( 0 , - dateColumn + 1).getValue();
   afterNightShiftList.push(afterNightShift)
  }
 
 let holidayFinder = data.createTextFinder('公').findAll();
 for ( let i = 0; i < holidayFinder.length; i++ ) {
   let holiday = holidayFinder[i].offset( 0 , - dateColumn + 1).getValue();
   holidayList.push(holiday)
  }
 
 let paidVacationFinder = data.createTextFinder('有').findAll();
 for ( let i = 0; i < paidVacationFinder.length; i++ ) {
   let paidVacation = paidVacationFinder[i].offset( 0 , - dateColumn + 1).getValue();
   paidVacationList.push(paidVacation)
  }

 var text = date +"("+week[targetDate.getDay()]+") 勤務者一覧"+"\n\n" +
     "早番 : " + earlyTurnList +"\n\n" +
     "日勤 : " + dayShiftList +"\n\n"  +
     "遅番 : " + lateTurnList + "\n\n" +
     "夜勤 : " + nightShiftList +"\n\n"+
     "明け : " + afterNightShiftList +"\n\n";
 
 
 if(holidayList.length + paidVacationList.length > 0){
  text = text +  "公・休・有 : \n" + holidayList+ "," + paidVacationList + "\n\n" ;
  }
    
 return text;
}

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