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

メッセージ送信機能の追加

前回の記事で、スプレッドシートとの連携(ユーザー登録、メッセージログ)機能のコードを書きました。

このあと、【勤務表からデータを取得】してそれをユーザーに【送信】するわけですが、まずはこの【送信】部分のコードを今回書いていこうと思います。

メッセージ送信のいろいろ

一口にメッセージを送信すると言っても、ユーザー全員に送るのか、特定のユーザーに送るのかで書き方が変わってきます。今回は、

①replyTokenへ送信(返信)

②ユーザー全員へ送信

③特定のユーザーへ送信

④特定のユーザー(管理者)へ送信

以上4つに分けて記述していこうと思います。

①replyTokenへの送信(返信)

メッセージイベントやフォローイベントが発生したときに、replyTokenというものが発行されます。ちょっと意味がわかりませんが、要するに「返信先」ってことなのだと思います。

この機能を使うと、友達登録されたときに登録ありがとうメッセージを送ったり、ユーザーからのメッセージに対して返信したりすることができます。

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);
}

④特定のユーザー(管理者)へ送信

最後に、管理者(自分)だけに送る関数も書いておきます。筆者はテスト送信するときなんかにこれを使います。コードの内容は③とほぼ一緒ですが、④行目の"ユーザーID"に自分のIDを入れる部分だけ違います。

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);
}

IDは前回、

ブロック、ブロック解除のテストをしたときに、スプレッドシートに記入されていると思います。

無題

青枠部分の文字列がIDです。これをコピーして、”ユーザーID”の部分に貼り付けてください。

メッセージ送信の注意点

LINEBotでメッセージを送るときに気をつけないといけない点があります。それは、上記の②~④の方法で送信する場合、無料枠が1000通/月に限られているということです。例えばユーザーが100人いる場合、”broadcast(全員)”では月に10通しか送れません。結構少ないです。

月に出勤日数が平均20日として、毎日全員にシフトを送るとすると、

1000通 / 20日 = 50人 が限度となりますが・・・

月によっては月末に1000通を超えて送信できなくなる

後々「勤務変更通知機能」を追加する予定なのですが、その分の「枠」も考えると、現実的には30人程度で無料枠は使い切ってしまう計算になります。

なので、一気に送信数が増える”broadcast”はできるだけ少なく使い、”reply”や"userId"を中心に使って送信数を抑える工夫もしなければいけません。

ちょっと不便なところも垣間見えてしまいましたが、一応抜け道もあります。例えば100人以上規模の大きな事業所でしたら、部署やチームという単位があると思います。公式アカウントを部署やチーム単位で1つずつ作ると、アカウントの数は増えますが、それぞれに1000通あてがわれますので、単純に送信できる数は増やせます。そんな工夫の仕方もあるということで。

テストの準備

前回書いた関数messageEventに少し書き加えます

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]);
}

前回ここまで書きました。最後の行( logSheet.getRange(lastRow+1,7).setValue(message[3]);の下)に以下のコードを加えてください。

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

"messageEvent"は、メッセージが来たら〇〇する、という関数ですが、これまではメッセージ内容のログを残すだけでした。今回足したのはメッセージの内容に対してのアクションの部分です。

「こんにちは」というメッセージには”sendLineMessageFromReplyToken”をつかって返事をする、返事の内容は「さようなら」、というコードです。

case文を使って書いているので、caseを増やせば他にもどんどん条件を追加していくことができます。

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;  
  }
}


テストしてみましょう

書けたら一旦保存して、ウェブアプリとして公開(新規)をしてからテストしてみましょう。

画像2

このように返事が返ってきたら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);
}

ずいぶん長くなってきました。ここまで書けただけでも素人的には満足なのですが、一番重要な中身となる部分が次回から登場するので、ラスボスまでしっかり書ききりたいと思います。


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