2020年6月13日土曜日

GASで簡単な予定通知LINE botを作ってみる。


先週ふと思い立ってLINE botを作ってみました。目的は度々発生するド忘れ防止です(笑)。

機能は、

・プッシュ通知でその日の予定を送ってくれる。
・リプライで今後の予定を教えてくれる。

というシンプルなものです。予定の登録はGoogle フォームから行います。

また副次的な機能としてユーザーIDの登録・削除機能を付けました。


LINE botもGAS(Google Apps Script)ならサーバーレスで作成可能です。

GASでは以前にメールで通知を送ってくれるものを作ったのですが、予定の確認にはLINEの方が手軽だし見やすいのでいいかもですね。

で、けっこう簡単にできたので、備忘録がてら手順を書いてみようと思います。

あらかじめ断っておきますと、とりあえず動けばいいやくらいの精度で作っておりますのでそういうものだと思って見てください(笑)。


***

① Google フォームとスプレッドシートの作成

今回のド忘れ防止LINE botでは、Google フォームから予定の内容と通知の日付を入力してスプレッドシートに送信するようにします。



こんな感じのフォームになります。「回答」→「回答先を選択」より新しいスプレッドシートを作成してください。

それからもうひとつ、ユーザーIDを登録しておくためのスプレッドシートを新規作成します。中身はまっさらで大丈夫です。


② LINE DevelopersとGASの設定・準備

次にLINE botを利用するために必要な設定と準備です。

こちらのサイトを参考にさせていただきました。途中手順で出てくるスプレッドシートはフォームから作成したものを利用してください。

【LINE BOT】30分からできるLINE BOT開発①【下準備】 | tomowarkar(※リンク切れしました)

【LINE BOT】30分からできるLINE BOT開発②【GASとLINEボットの紐付け】 | tomowarkar(※リンク切れしました)

突っかかったらこちらも。

【LINE Botの作り方】Messaging API × GAS(Google Apps Script)でおうむ返しボットを作成する | TAKEIHO


③ コードの作成

フォームから作成したスプレッドシートの「ツール」→「スクリプト エディタ」でGASを開きます。

コードは以下の通り。

var ACCESS_TOKEN = "チャンネルアクセストークン";
var PUSH = "https://api.line.me/v2/bot/message/multicast";
var REPLY = "https://api.line.me/v2/bot/message/reply";

function doPost(e) {
    var events = JSON.parse(e.postData.contents).events;
    events.forEach(function(event) {
      if(event.type == "message") {
        reply(event);
      } else if(event.type == "follow") {
        follow(event);
      } else if(event.type == "unfollow") {
        unFollow(event);
      }
    });    
}

function push() {
    var spreadsheet = SpreadsheetApp.openById("予定のスプレッドシートID");
    var sheet = spreadsheet.getActiveSheet();
 
    var start = 2;
    var end = sheet.getLastRow();
 
    var today =  new Date();  
    var formattedDate = Utilities.formatDate(today, "JST", "yyyy/MM/dd");
  
    var spsh = SpreadsheetApp.openById("ユーザーIDのスプレッドシートID");
    var sht = spsh.getActiveSheet();
    var data = sht.getDataRange().getValues();
    var userlist = [];
    for(var n = 0; n < data.length; n++){
      userlist.push(data[n][0]);
    }      
  
    var value =[];  
    for(var i = start; i <= end; i++) {
      var sell_C = "C" + i;
      var day_C = sheet.getRange(sell_C).getValue();
      var day_C = Utilities.formatDate(day_C, "JST", "yyyy/MM/dd");      
      if(formattedDate != day_C){
        continue; 
      }    
      var sell_B = "B" + i;
      value.push("・" + sheet.getRange(sell_B).getValue() + "\n\n");
    }
    if(value.length == 0){
      return;
    }
  
    var postData = {
      "to" : userlist,
      "messages" : [
        {
            "type" : "text",
            "text" : "【本日の予定】\n\n" + value.join("") + "今日も一日がんばりましょう!"
        }
      ]
    };
  
    var headers = {
        "Content-Type" : "application/json; charset=UTF-8",
        "Authorization" : "Bearer " + ACCESS_TOKEN
    };
  
    var options = {
        "method" : "POST",
        "headers" : headers,
        "payload" : JSON.stringify(postData)
    };
    return UrlFetchApp.fetch(PUSH, options);
}

function reply(data) {
    var postMsg = data.message.text;
    var replyToken = data.replyToken;
    var replyText = "";

    if(postMsg == "登録") {
      replyText = "予定の登録はこちら\n" + "Google フォームのURL";
    } else if(postMsg == "今後の予定") {     
      var spreadsheet = SpreadsheetApp.openById("予定のスプレッドシートID");
      var sheet = spreadsheet.getActiveSheet();
  
      var range = sheet.getRange(2, 1, sheet.getLastRow(), 3);    
      range.sort([
        {column: 3, ascending: true},  
      ]);
      
      var start = 2;
      var end = sheet.getLastRow();
  
      var today =  new Date();  
      var formattedDate = Utilities.formatDate(today, "JST", "yyyy/MM/dd");
        
      var value =[];  
      for(var i = start; i <= end; i++) {
        var sell_C = "C" + i;
        var day_C = sheet.getRange(sell_C).getValue();
        var day_C = Utilities.formatDate(day_C, "JST", "yyyy/MM/dd");      
        if(formattedDate < day_C){
          var sell_B = "B" + i;
          value.push("・" + sheet.getRange(sell_B).getValue() + "(" + day_C + ")" + "\n\n");
        }
      }
      replyText = value.join("") + "忘れないようにしましょう!";
    }

    if(replyText == ""){
        return;
    } else {
        var postData = {
          "replyToken" : replyToken,
          "messages" : [
              {
                  "type" : "text",
                  "text" : replyText
              }
          ]
        };

        var headers = {
            "Content-Type" : "application/json; charset=UTF-8",
            "Authorization" : "Bearer " + ACCESS_TOKEN
        };

        var options = {
            "method" : "POST",
            "headers" : headers,
            "payload" : JSON.stringify(postData)
        };

        return UrlFetchApp.fetch(REPLY, options);      
    }
}

function follow(e) {
  var spsh = SpreadsheetApp.openById("ユーザーIDのスプレッドシートID");
  var sht = spsh.getActiveSheet();
  sht.appendRow([e.source.userId]);
}

function unFollow(e){
  var spsh = SpreadsheetApp.openById("ユーザーIDのスプレッドシートID"); 
  var sht = spsh.getActiveSheet();
  var result = findRow(sht, e.source.userId, 1);
  if(result > 0){
    sht.deleteRows(result);
  }
}

function findRow(sht,val,col){
  var data = sht.getDataRange().getValues(); 
  for(var i = 0; i < data.length; i++){
    if(data[i][col-1] === val){
      return i+1;
    }
  }
  return 0;
}

コピペで使えます。

以下の4つの値(全7ヵ所)は固有になりますので書き換えてください。

・1行目の「チャンネルアクセストークン」
LINE Developersの該当するチャンネルのMessaging API設定で発行したものです。

・19、82行目の「予定のスプレッドシートID」
・28、138、144行目の「ユーザーIDのスプレッドシートID」
該当するスプレッドシートを開いた状態でアドレスバーURLの下記部分に表示されます。ちょっと長めです。

https://docs.google.com/spreadsheets/d/ここの値をコピペ/edit#gid=0

・80行目の「Google フォームのURL」
Google フォームのURLですね。アドレスバーをまるっとコピペ。


参考にしたサイトはこちら

【入門】Google Apps ScriptでLINE botを作成する - Qiita

【LINE Botの作り方】Python × Messaging APIでプッシュ通知を行うボットを作ろう | TAKEIHO


ちなみにGASの初回実行時に承認が必要となります。

無料のGoogleアカウントの場合「このアプリは確認されていません」とちょっとこわい?ページが表示されますが大丈夫です。ここで「安全なページに戻る」をクリックすると実行できませんので、「詳細」→「プロジェクト名(安全ではないページ)に移動」から承認します。

下のサイトが参考になります。

【初心者向けGAS】スクリプト実行時の「承認」でびっくりしないために


④ トリガーの設定


push関数を毎日定期的に実行するためにトリガーの設定をします。時間などは適宜変更してください。

「トリガーを追加」より、

・「実行する関数を選択」→ push

・「実行するデプロイを選択」→ Head

・「イベントのソースを選択」→ 時間主導型

・「時間ベースのトリガーのタイプを選択」→ 日付ベースのタイマー

・「時刻を選択」→ 午前7時〜8時

・「エラー通知設定」→ 毎日通知を受け取る

これで予定がある日は通知がされます。


⑤ ウェブアプリケーションとして公開

コードが完成しましたら「公開」→「ウェブアプリケーションとして導入…」より、

・「Project version: 」→ new
(「プロジェクトバージョン: 」→ new

・「Execute the app as: 」→ Me(自分のGmailアドレス))
(「次のユーザーとしてアプリケーションを実行: 」→ 自分(自分のGmailアドレス)

・「Who has access to the app: 」→ Anyone, even anonymous
(アプリケーションにアクセスできるユーザー: 」→ 全員(匿名ユーザーを含む)

で更新してください。

カッコ内は日本語表記の場合です。(私は英語表記で出てくるんですが何でだろう?)


⑥ さっそく使ってみる

ということで完成です。ボットを友達に追加しますとユーザーIDがスプレッドシートに登録されプッシュ通知が届くようになります。(ブロックすれば削除されます。)



うまくいくとこんな感じになります。その日に予定があれば【本日の予定】としてトリガーを設定した時間に配信されます。

今後の予定」と送ると翌日以降の予定を順番に並べてリプライくれます。

登録」と送ると予定を入力するGoogle フォームのリンクをリプライくれます。


***

どうでしょうか、ちゃんと動いたでしょうか?カスタマイズもいろいろできるかと思います。

GAS、本当に便利ですね。作業が捗ります。


Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。