#GASでLINEリマインダーを作る
##動機
大学サークルの日程調整をよくLINEで行い、リマインドを毎回行うのが面倒で「自動化できたらな」と思っていたので作成に至った。
##目標
####学習の目標
GASの基本をマスター
- Javascriptの書き方
- Google Apps Scriptの書き方
- 動的型付け言語の書き方
####プログラムの目標
- 予定の追加(Google Form)
- 予定の記録(SpreadSheet)
- 予定の確認(GAS)
- 予定のリマインド(LINE)
以上を有するLINEBotを作る。
##筆者のスペック
- 大学の情報系2回生
- C,C++,Javaを講義で学んだ
記事は、ある程度プログラミングを学んだことのある人向けに。
自分の復習も兼ねて書きます。
#設計
####予定登録
#####トリガー
GASにはトリガー(引き金)と呼ばれる機能があります。
Googleフォームのスクリプトエディタで作成したスクリプトには、イベントのソースを選択に「時間主導型」の他にフォーム「フォームから」という選択肢がありこれを選択するとフォーム送信時にスクリプトを起動させる事ができます。
####1.フォーム作成
通常通り、Google Formを作成していきます。
- 予定名
- 場所
- 予定の説明
- 終日or時間指定(2つに対応させるためどちらか選択できるようにする)
- 開始日と終了日(時間指定の分岐は時間も記述)
- 登録者名(誰が作成したかわかるように)
その他、念の為メールアドレスを収集しておきます。(事前に断っておきましょう.)
####2.SpreadSheet記録
Google Formを作成すると自動でSpreadSheetが作成されます。
実際のスプレッドシートです。
MARK START END は現時点で気にしないでください。(上記のフォームを作成しただけだと作成されません。)
####3.いよいよScriptを書く
//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()関数以下のプログラムを以下に示します。
///////////////////////////////////////////////////////////////////////////////////////////////////
//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'日'");
//終日で1日だけの日程かどうかの判定
//日程の〇月〇日~〇月〇日とするか、◯月◯日のみの表示かを分岐する。
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リクエスト受信で予め取得しておき、そこだけに送信するようにしています。
*1のサイトのdoPost(e)を参考にUSERIDをSpreadSheetに記録するようにしました。
LINE MessageAPI側の操作は
https://note.com/data1/n/nc52a54e5a679
を参考に行った。
(ry
###おまけ
Googleカレンダーとせっかく連携できたので、Googleカレンダーにメンバにも予定を追加してほしいと思いながら、共有カレンダーだと自分の予定も見えてしまうので各自でURLで追加してもらうことにした。
googleカレンダーサイトのURLを自動生成できるらしいので、やってみた。
///////////////////////////////////////////////////////////////////////////////////////////////////
//カレンダー作成////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//タイムスタンプ メールアドレス 予定名 場所 予定の説明 イベントの長さ 開始日程 終了日程 開始日程(終日) 終了日程 登録者名
// 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日加算することで対応しました。
以上で1~3までの機能が出来上がりました。これらをフォーム実行時にトリガーとして実行させます。
4番の解説は、
https://qiita.com/1_s_n/items/f6c02a80b4928bddcc8c
にて紹介しています!