RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【GAS】LINE Messaging APIでリマインダーを作ってみた。

f:id:tech-rakus:20200424152339j:plain

はじめに

こんにちは。新卒2年目のrs_chankoです。
まだまだ慣れない社会人。そんな中で思ったことがあります。


物忘れが酷すぎる。


これは元々分かっていたんですが、最近特に思います。


会社では朝にスケジュールを確認しますが、
プライベートではスケジュールを見る習慣がなく、忘れてしまいがち…。


「そうだ、LINEなら毎日見るし、自分で作っちゃうか。」

そう思い立ち、GASとLINE Messaging APIを使ってリマインダーを作ることになりました。





実装に使った物





実装する機能

  • 日付と予定をスプレッドシートに登録すると、登録されたデータを参照してメッセージとして送ってくれる
  • (忘れがちなので)7時、12時、19時に今日と明日の予定を教えてくれる



    リマインダーを実装してみる

    LINE Messaging APIとGASの接続方法は先輩がすでに書いていたのでそのまま参考にしました。
    tech-blog.rakus.co.jp
    しかしそもそもJavaScriptもあまりちゃんと書いたことがなく、GASやMessaging APIも使ったことがない。
    ひとまず練習するか、ということでお試しにオウム返しbotを作ってみました。

    note.com
    これは簡単にできました。




    さて、本題です。


    ひとまずオウム返しで使ったAPI周りのコードを元に実装していきます。

//LINEのアクセストークン
var channel_access_token = "アクセストークン";
var headers = {
   "Content-Type": "application/json; charset=UTF-8",
   "Authorization": "Bearer " + channel_access_token
};

//返信する
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);
}

//指定のuserIdにメッセージを送る
function sendLineMessageFromUserId(userId, text) {
 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);
}




準備が整いました。



それではまずBotにやってほしいことをまとめます。

  • 予定の登録
  • 予定の削除
  • 登録・削除時の返事
  • 指定時刻の通知
  • 登録されている予定の確認

上記ができるよう実装します。


//8桁日付のフォーマット
function formatDateForDisplay (date){
  var year = date.substr(0,4);
  var month = date.substr(4,2);
  var day = date.substr(6,2);
  var result = year + "/" + month + "/" + day;
  return result;
}

//Date型日付のフォーマット
function formatDate (date, format) {
  format = format.replace(/yyyy/g, date.getFullYear());
  format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
  format = format.replace(/dd/g, ('0' + date.getDate()).slice(-2));
  return format;
};

//日付をシートに記載する
function setDate(date, plan, row) {
  if (isNaN(date) || String(date).length != 8) {
    return '日付が正しくありません';
  }
  setFromRowAndLine(date, row, 0);
  return setPlan(plan, row);
}

//予定をシートに記載する
function setPlan(plan, row) {
  setFromRowAndLine(plan, row, 1);
  return '予定を登録しました。';
}

//トリガー起動で当日と翌日の予定を返す
function getTodayAndNextDayMessage() {
  var date = new Date();
  var userId = ’LINEのユーザーID’;
  //今日の予定を取得
  var remindText = getDatePlan(date);
  //明日の予定を取得
  date.setDate(date.getDate() + 1);
  remindText += "\n" + getDatePlan(date);
  return sendLineMessageFromUserId(userId, remindText);
}

//指定日の予定を返す
function getDatePlan(date) {
  if(date instanceof Date) {
    date = formatDate(date, 'yyyyMMdd');
  }
  var day = formatDateForDisplay(date);
  var remindText = day + "\n";
  var plans = searchPlans(date);
  for(var i = 0; i < plans.length; i++) {
    remindText += getFromRowAndLine('webhook', plans[i]);
    remindText += "\n";
  }
  remindText = remindText.slice(0,-1);
  if(remindText.length === 10) {
    remindText += "\n明日は予定がありません。";
  }
  return remindText;
}

//受け取ったメッセージに対して返信する
function doPost(e) {
  var webhookData = JSON.parse(e.postData.contents).events[0];
  var message, replyToken, replyText, userId;
  message = webhookData.message.text.split("\n");
  replyToken = webhookData.replyToken;
  userId = webhookData.source.userId;
  var processing = message[0];
  var planDate = message[1];
  var plan = message[2];
  var row = getLastRow();
  
  switch (processing) {
    case '登録':
      replyText = setDate(planDate, plan, row);
      break;
    case 'キャンセル':
      replyText = cancel(planDate, plan);
      break;
    case '確認':
      replyText = planDate === undefined ? getDatePlan(new Date()) : getDatePlan(planDate);
      break;
    case 'test':
      replyText = testSheet();
      break;
    default:
      replyText = "予定の登録・キャンセルができます。\n\n登録\n20200101\n予定\n\nの形式で入力してください。";
      break;
  }
  return sendLineMessageFromReplyToken(replyToken, replyText);
}

//予定を削除する
function cancel(date,plan) {
 deleteRowOfDateAndPlan(date, plan);
 return '予定の登録をキャンセルしました。'
}

//トリガーをセットする
function setTrigger(){
  const time = new Date();
  time.setHours(7);
  time.setMinutes(00);
  ScriptApp.newTrigger('getTodayAndNextDayMessage').timeBased().at(time).create();
  time.setHours(12);
  ScriptApp.newTrigger('getTodayAndNextDayMessage').timeBased().at(time).create();
  time.setHours(19);
  ScriptApp.newTrigger('getTodayAndNextDayMessage').timeBased().at(time).create();
  time.setDate(time.getDate() - 1);
  //前日の予定を削除する
  deleteRowOfDate(formatDate(time, 'yyyyMMdd'));
}

//トリガーを削除する
function delTrigger() { 
  const triggers = ScriptApp.getProjectTriggers();
  for(const trigger of triggers){
    if(trigger.getHandlerFunction() == "getTodayAndNextDayMessage"){
      ScriptApp.deleteTrigger(trigger);
    }
  }
}



このコードではLINEで実際に送るメッセージの生成・トリガーのセット・削除をしています。
GASのトリガーって定期実行をすると1時間の範囲内で実行されるらしいんですよね。
ちゃんと同じ時間に送ってほしいので、今回は指定の時間に通知用のメソッドをトリガーとしてセットするメソッドを定期実行にしました。



f:id:rs_chanko:20200422095346p:plain


スプレッドシートとのテキストのやりとりは下記の実装をします。

//スプレッドシート
var spreadsheet = SpreadsheetApp.openById("スプレッドシートのID");
var sheet = spreadsheet.getSheetByName('スプレッドシートのシート名');

//受け取った日付の予定が記載されている行を返す
function searchPlans(date) {
 var data = sheet.getDataRange().getValues();
 var plans = [];
 for (var i = 0; i < data.length; i++) {
   if (data[i][0] == date) {
     plans.push(i + 1);
   }
 }
  return plans;
}

//列を指定してデータを取得する
function getFromRowAndLine(sheetName, row) {
 var data = sheet.getDataRange().getValues();
 return data[row - 1][1];
}

//列を指定してデータを書き込む
function setFromRowAndLine(val, row, line) {
 sheet.getRange(row + 1, line + 1).setValue(val);
}

function getLastRow() {
  return sheet.getLastRow();
}

//日付と予定から行を削除する
function deleteRowOfDateAndPlan(date, plan) {
  var lastRow = getLastRow();
  for (var i = 1; i <= lastRow; i++) {
    if (sheet.getRange(i, 1).getValue() == date && sheet.getRange(i, 2).getValue() == plan) {
      sheet.deleteRows(i);
    }
  }
}

//指定の日付の行を削除する
function deleteRowOfDate(date) {
  var lastRow = sheet.getDataRange().getLastRow();
  for (var i = 1; i <= lastRow; i++) {
    if (sheet.getRange(i, 1).getValue() == date) {
      sheet.deleteRows(i);
    }
  }
}



これで実際にスプレッドシートに日付と予定を書き込んだり、参照することができるようになりました。


f:id:rs_chanko:20200421204951p:plain


こんな感じでLINEから送られてきた予定が書き込まれます。




動作確認!

それでは実際にメッセージを見てみましょう。


f:id:rs_chanko:20200424160945j:plain

おおおお!
予定の登録や確認ができ、19時に予定の通知がきました!
そんなこんなで完成です。




おわりに

「リマインダー」について何も調べていなかったんですが、
作り終わってから調べたところ、すでにリマインドBotが存在していました。。。

remine.akira108.com

しかし作ってしまったものは作ってしまったので、今後はカスタマイズをして自分の好みのリマインダーを作っていこうと思います。
あとはLINEに予定を登録することを忘れないように…。



参考にした記事

qiita.com

tonari-it.com

Copyright © RAKUS Co., Ltd. All rights reserved.