GASでLINEリマインダーを作る。

#GASでLINEリマインダーを作る
##動機
大学サークルの日程調整をよくLINEで行い、リマインドを毎回行うのが面倒で｢自動化できたらな｣と思っていたので作成に至った。

##目標
####学習の目標
GASの基本をマスター

- Javascriptの書き方
- Google Apps Scriptの書き方
- 動的型付け言語の書き方

####プログラムの目標
1. 予定の追加(Google Form)
- 予定の記録(SpreadSheet)
- 予定の確認(GAS)
- 予定のリマインド(LINE)

以上を有するLINEBotを作る。

##筆者のスペック

- 大学の情報系2回生
- C,C++,Javaを講義で学んだ

記事は、ある程度プログラミングを学んだことのある人向けに。
自分の復習も兼ねて書きます。

#設計
####予定登録
#####トリガー
GASにはトリガー(引き金)と呼ばれる機能があります。
Googleフォームのスクリプトエディタで作成したスクリプトには、イベントのソースを選択に｢時間主導型｣の他にフォーム｢フォームから｣という選択肢がありこれを選択するとフォーム送信時にスクリプトを起動させる事ができます。

####1.フォーム作成
通常通り、Google Formを作成していきます。
- 予定名
- 場所
- 予定の説明
- 終日or時間指定(２つに対応させるためどちらか選択できるようにする)
- 開始日と終了日(時間指定の分岐は時間も記述)
- 登録者名(誰が作成したかわかるように)
その他、念の為メールアドレスを収集しておきます。(事前に断っておきましょう.)

####2.SpreadSheet記録
Google Formを作成すると自動でSpreadSheetが作成されます。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596630/0b4b96f4-8bf9-b3dc-42bf-5de51d5e3019.png)
実際のスプレッドシートです。
MARK START END は現時点で気にしないでください。(上記のフォームを作成しただけだと作成されません。)

####3.いよいよScriptを書く

```javascript:yotei_reg(1)
//1               2            3     4      5            6         7         8          9          10      11
//タイムスタンプ	メールアドレス	予定名	場所	予定の説明	イベントの長さ	開始日程	終了日程	開始日程	終了日程	登録者名
// 0              1             2    3     4             5         6         7          8           9       10

function submitForm(e) {
  
  var today=new Date();
  
  var spreadsheet = SpreadsheetApp.openById('/*-スプレッドシートのID-*/');
  var sheet = spreadsheet.getSheetByName('フォームの回答 1');
  var lastrow=sheet.getLastRow();
  
  var ans=[]
  ans=sheet.getRange(lastrow,1,1,11).getValues();
  //終日と指定日の振り分け
  //
  if(ans[0][6]===undefined || ans[0][6]==''){
    ans[0][6]=ans[0][8];
    ans[0][7]=ans[0][9];
  }
  
  
//
//カレンダー登録部
//
  var calendar = CalendarApp.getDefaultCalendar();
  var rtoday=Utilities.formatDate(today, "JST", "yyyy'年'MM'月'dd'日'")
  var option={
    description:ans[0][4]+'登録者:'+ans[0][10]+rtoday+'\n\nRegistered by Google Form',
    location:ans[0][3]
  }
  ans[0][6]=new Date(ans[0][6]);
  ans[0][7]=new Date(ans[0][7]);
  
//Formで登録した人のカレンダーに追加
  calendar.createEvent(ans[0][2],ans[0][6],ans[0][7], option);
  
//LINEメッセージ送信(別関数)
  pushMessage(ans);
}

```
フォーム起動時の動作は主に、フォームに入力した人自身のGoogleカレンダーに予定を追加することです。
試験的に(思いつき)で実装したものですが、GASでは結構簡単に実装できて驚きました。

pushMessage()は予定が登録された時にLINEに送信します。
pushMessage()関数以下のプログラムを以下に示します。

```javascript:yotei_reg(2)

///////////////////////////////////////////////////////////////////////////////////////////////////
//LINE送信部////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

//グローバル関数
var CHANNEL_ACCESS_TOKEN = '/*MessageAPIアクセストークン*/'; 
var USER_ID = '/*送信先UserID*/'; //*2

//タイムスタンプ	メールアドレス	予定名	場所	予定の説明	イベントの長さ	開始日程	終了日程	開始日程	終了日程	登録者名
// 0              1             2    3     4             5         6         7          8           9       10

function pushMessage(e) {
  var msg='あ'; //本文用変数(とりあえずStringで初期化)


  var date1=Utilities.formatDate(e[0][6] ,"JST", "yyyy'年'MM'月'dd'日'");
  var date2=Utilities.formatDate(e[0][7] ,"JST", "yyyy'年'MM'月'dd'日'");

//終日で１日だけの日程かどうかの判定
//日程の〇月〇日~〇月〇日とするか、◯月◯日のみの表示かを分岐する。
  if(date1!=date2){
    
    msg='【'+e[0][2]+'】\n'
          +'以下のイベントが登録されました。\n'
          +'【名前】'+e[0][2]+'\n'
          +'【場所】'+e[0][3]+'\n'
          +'【日程】'+ Utilities.formatDate(e[0][6] ,"JST", "MM'月'dd'日'")+'~'+Utilities.formatDate(e[0][7], "JST", "MM'月'dd'日'")+'\n'
          +'【詳細】'+e[0][4]+'\n\n\n'
          +'他の日程は以下から確認できます↓↓↓\n'
          +'/*予定を吐き出すサイトURL*/'
          +'\nGoogle カレンダー追加は以下から\n'
          +RegistURL(e);
    
    
  }else{
    msg='【'+e[0][2]+'】\n'
          +'以下のイベントが登録されました。\n'
          +'【名前】'+e[0][2]+'\n'
          +'【場所】'+e[0][3]+'\n'
          +'【日程】'+Utilities.formatDate(e[0][6], "JST", "MM'月'dd'日'")+'\n'
          +'【詳細】'+e[0][4]+'\n'
          +'他の日程は以下から確認できます↓↓↓\n'
          +'/*予定を吐き出すURL*/';
          +'\nGoogle カレンダー追加は以下から\n'
          +RegistURL(e);
    
  }
//*1
  var postData = {
    "to": USER_ID, 
    "messages": [{
      "type": "text",
      "text": msg,
    }]
  };

  //共通URL
  var url = "https://api.line.me/v2/bot/message/push";
  var headers = {
    "Content-Type": "application/json",
    'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
  };

  var options = {
    "method": "post",
    "headers": headers,
    "payload": JSON.stringify(postData)
  };
  var response = UrlFetchApp.fetch(url, options);
}

```
*1
以下のサイトを参考にした。
https://qiita.com/ttexan/items/749bed9a60313e51b4c8

*2
UserIDはLINEIDではない。公式アカウントと個別ユーザに紐付いたIDである。
友達登録をしていないユーザには送信できない。

LINE messageAPI利用にはAPIキーの取得と、送信先のUSERIDを取得しておく必要があります。
ユーザを区別する必要が無い場合はSpreadSheetやプログラムに書く必要がなく
postリクエストで送られてきたIDでそのまま返信すれば良いのですが、
サークルの予定なので外部にホイホイ投げるわけにも行かないので、GROUPID(USERID)をPOSTリクエスト受信で予め取得しておき、そこだけに送信するようにしています。

*１のサイトのdoPost(e)を参考にUSERIDをSpreadSheetに記録するようにしました。

LINE MessageAPI側の操作は
https://note.com/data1/n/nc52a54e5a679
を参考に行った。
(ry

###おまけ
Googleカレンダーとせっかく連携できたので、Googleカレンダーにメンバにも予定を追加してほしいと思いながら、共有カレンダーだと自分の予定も見えてしまうので各自でURLで追加してもらうことにした。
googleカレンダーサイトのURLを自動生成できるらしいので、やってみた。

```javascript:yoteireg(3)

///////////////////////////////////////////////////////////////////////////////////////////////////
//カレンダー作成////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

//タイムスタンプ	メールアドレス	予定名	場所	予定の説明	イベントの長さ	開始日程	終了日程	開始日程(終日)	終了日程	登録者名
// 0              1             2    3     4             5         6         7          8                9       10
function RegistURL(ans){
  Logger.log(ans[0][6]);
  
  var DateS= new Date(ans[0][6]);
  var DateE= new Date(ans[0][7]);
  
  
  
    if(ans[0][5]=="終日"){
      
    DateE.setDate(DateE.getDate() + 1);  //bag対応(不具合あったら消す)
      
    DateS=Utilities.formatDate(DateS,"JST","yyyyMMdd");
    DateE=Utilities.formatDate(DateE,"JST","yyyyMMdd"); 
      
    }
  
  else{
    DateS=Utilities.formatDate(DateS,"JST","yyyyMMdd'T'HHmmss");
    DateE=Utilities.formatDate(DateE,"JST","yyyyMMdd'T'HHmmss"); 
  }
    
    var text=ans[0][2];
    var location=ans[0][3];
    var details=ans[0][4];
  
  if(location==""){
    location=" ";
  }
   if(details==""){
    details="詳細情報はありません";
  }
    
  return 'https://www.google.com/calendar/event?action=TEMPLATE'+
    '&text='    + encodeURIComponent(text) +
    '&dates='   + DateS + '/' + DateE+
    '&details=' + encodeURIComponent(details) +
    '&location='+ encodeURIComponent(location);
}

```

*3
以下のサイトを参考にした
https://qiita.com/s-mori/items/24c128e0b0d841036dd8

何故か、終了日程に-１日たされるというバグが有ったので1日加算することで対応しました。

以上で1~3までの機能が出来上がりました。これらをフォーム実行時にトリガーとして実行させます。

４番の解説は、
https://qiita.com/1_s_n/items/f6c02a80b4928bddcc8c
にて紹介しています！