【GAS】Googleカレンダーの予定をLINEで受け取る

#はじめに
Googleカレンダーに登録してある今日の予定、また明日の予定を一定の時刻にLINEで受け取るというものをつくっていきます。

私自身初心者なのでプロジェクト作成方法や設定の仕方等も書いていきます。
なお、コードは[最後](#まとめ)にまとめてあります。

#完成イメージ
|<img src="https://qiita-image-store.s3.amazonaws.com/0/394580/dc0d3d9b-53de-cf79-4006-18ce3183c6a4.png" width="500px">
|:-:|

①朝、Googleカレンダーの今日の予定をLINEで受け取る。
②夕方、Googleカレンダーの明日の予定をLINEで受け取る。

＜送る内容＞
・日時
・タイトル
・場所（登録があったら）
・説明（登録があったら）
なお、予定が無ければ無いよと送る。

<details><summary>使用するカレンダー</summary><div>
![q_s_cal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/d7f447aa-1e94-9fd4-094c-c42b916024c6.png)
</div>
</details>

#設定
###プロジェクトの新規作成
まず、最初の状態では入っていないGoogle Apps Scriptを追加します。
Googleドライブ > 新規 > ①その他 > ②アプリを追加

|![q_s_1.png](https://qiita-image-store.s3.amazonaws.com/0/394580/cc6920aa-82ba-4554-bc36-776762a98cfd.png)
|:-:|

Google Apps Script を探して ③接続

|![q_s_2.png](https://qiita-image-store.s3.amazonaws.com/0/394580/b027c35b-cbbc-47af-8681-7b51d7d1aace.png)
|:-:|

▼▼▼

|![q_s_3.png](https://qiita-image-store.s3.amazonaws.com/0/394580/3c6caf03-0537-8660-694d-08d13fff7cf8.png)
|:-:|

追加できたので早速プロジェクトを新規作成します。
Googleドライブ > 新規 > ④その他 > ⑤Google Apps Script

|![q_s_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/8d6cd07e-fb18-1ba1-1c84-f6fc1669a2ce.png)
|:-:|


ひとまずプロジェクトに名前を付けます。
ファイル > 名前の変更 でも、画面上部のプロジェクト名をクリックでも変えられます。

|![q_s_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/8e670c6a-3413-8961-8537-7c9230ba8259.png)
|:-:|

スクリプトにも。

|![q_s_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/de4d4a67-a706-0809-601f-defd78bb1d5d.png)
|:-:|

|![q_s_7.png](https://qiita-image-store.s3.amazonaws.com/0/394580/fdc83996-6513-afb5-b7ef-278bd4444fa4.png)
|:-:|

以上でプロジェクトの新規作成は完了です。

###LINEとの連携設定
LINE Notifyとの連携が必要になるので、その辺りを設定していきます。

[こちら](https://notify-bot.line.me/ja/)にアクセスし、 ①ログイン します。
※LINEアプリの方でメールアドレスの設定とログイン許可の設定が必要です。

|![q_s_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/acf5d11b-3ddf-06a7-cff3-b5f1ae72c595.png)
|:-:|

|![q_s_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/ffed9a31-9542-bb32-7705-a36345b936a0.png)
|:-:|

ログインしたら ②マイページ > ③トークンを発行する

|![q_s_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/76cba393-f444-7f5c-d982-58c0af931da0.png)
|:-:|

|![q_s_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/05d6b428-4c73-fd16-14b7-83356a89e74c.png)
|:-:|

トークンの設定をしていきます。
④トークン名：自由に入力できます。ちなみにココに出ます。 [^1]
⑤トークルームを選択：今回は『1:1でLINE Notifyから通知を受け取る』を選択します。
完了したら ⑥発行する をクリックします。

|![q_s_14.png](https://qiita-image-store.s3.amazonaws.com/0/394580/08bec0c4-2924-5bc1-b582-9c381bc7b572.png)
|:-:|

▼▼▼

|![q_s_15.png](https://qiita-image-store.s3.amazonaws.com/0/394580/1d470dec-2a92-fe05-335f-b856ad4f0447.png)
|:-:|

⑦コピー したトークンを使っていきましょう。

以上でLINEとの連携設定は完了です。

###プロパティの設定
直打ちを避けたいので、先ほどのトークンはプロパティに設定します。
ファイル > ①プロジェクトのプロパティ

|![q_s_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/26ba0847-9e58-1ef6-d723-afac1ec2b26b.png)
|:-:|

②スクリプトのプロパティ > ③行を追加 > さっきのトークンを設定 > ④保存

|![q_s_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394580/5d277a86-d71e-29ac-490e-0d07eb41be28.png)
|:-:|

以上でプロパティの設定は完了です。

#コード
準備が整ったのでコードを書いていきましょう。

###メイン処理
```js:main.gs
function sendTodaySchedule() {
  var accessToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  // 引数により取得する日付を変えられるようにしたいので、
  // 今日から何日後かを渡すようにします。
  var message = getMessage(0);
  var options =
   {
     'method'  : 'post'
    ,'payload' : 'message=' + message
    ,'headers' : {'Authorization' : 'Bearer '+ accessToken}
    ,muteHttpExceptions:true
   };
  UrlFetchApp.fetch('https://notify-api.line.me/api/notify',options);
}
```

###カレンダー内容の取得処理
```js:main.gs
/**
* メッセージ内容取得
* @param {number} 今日起算の日数
* @return {string} メッセージ内容
*/
function getMessage(prm) {
  const week = ['日','月','火','水','木','金','土'];
  var cal = CalendarApp.getCalendarById('*****************@gmail.com');
  var date = new Date();
  var strBody = '';
  var strHeader = '';
  // タイトル
  if ( prm==0 ) {
    strHeader = '今日 ';
  } else if ( prm==1 ) {
    strHeader = '明日 ';
  } 
  date = new Date(date.getYear(),date.getMonth(),date.getDate() + prm);
  strHeader += Utilities.formatDate(date,'JST','yyyy/M/d')
                + '(' +week[date.getDay()] + ') の予定\n';
  // 内容
  strBody = getEvents(cal,date);
  if ( _isNull(strBody) ) strBody = '予定はありません。';
  return strHeader + strBody;
}

/**
* カレンダーイベント内容取得
* @param {object} カレンダー
* @param {date} 日付
* @return {string} イベント内容
*/
function getEvents(prmCal,prmDate) {
  var strEvents = '';
  var strStart = '';
  var strEnd = '';
  var strTime = '';
  var strLocation = '';
  var strDescription = '';
  if ( !_isNull(prmCal) ) {
     var arrEvents = prmCal.getEventsForDay(new Date(prmDate));
     for (var i=0; i<arrEvents.length; i++) {
       if ( !_isNull(strEvents) ) strEvents += '\n';
       strStart = _HHmm(arrEvents[i].getStartTime());
       strEnd = _HHmm(arrEvents[i].getEndTime());
       if ( strStart===strEnd ) {
         strTime = '終日';
       } else {
         strTime = strStart + '～' + strEnd;
       }
       strEvents += '･' + strTime + '【' + arrEvents[i].getTitle() + '】';
       strLocation = arrEvents[i].getLocation();
       strDescription = arrEvents[i].getDescription();
       if ( !_isNull(strLocation) ) strEvents += '\n　場所：' + strLocation;
       if ( !_isNull(strDescription) ) strEvents += '\n　説明：' + strDescription;
     }
  }
  return strEvents;
}
/**
* 時刻フォーマット
*/
function _HHmm(str){
  return Utilities.formatDate(str,'JST','HH:mm');
}
/**
* NULL判定
* @param {object} 判定対象
* @return {bool} NULLの場合TRUE
*/
function _isNull(prm) {
  if ( prm=='' || prm===null || prm===undefined ) {
    return true;
  } else {
    return false;
  }
}
```

難しいことはしていませんが、開始時刻と終了時刻が等しいと終日となるのははじめて知りました。
ということで、ひとまずメッセージ内容をログに出してみます。

```js:main.gs
function test(){
  var message = getMessage(0);
  Logger.log('今日\n' + message);
  message = getMessage(1);
  Logger.log('\n明日\n' + message);
}
```

▼▼▼

|![q_s_16.png](https://qiita-image-store.s3.amazonaws.com/0/394580/5cfa4d51-6bec-8dce-8cf7-7dc1716b8c2f.png)
|:-:|

では改めて ` sendTodaySchedule() ` を実行しましょう。

なお、初回だと承認が必要になります。
①許可を確認 > 「アカウントを選択」にて、承認するアカウントを選択 > ②許可 とすれば承認されます。

|![q_s_oath_1.png](https://qiita-image-store.s3.amazonaws.com/0/394580/9cc03b31-d899-f90f-cbc5-5f0132d9ea5f.png)
|:-:|

|![q_s_oath_2.png](https://qiita-image-store.s3.amazonaws.com/0/394580/b71da762-bea9-b421-9632-dc4e4ef17cfe.png)
|:-:|

ということで

|![line.png](https://qiita-image-store.s3.amazonaws.com/0/394580/e1bcbff6-b23b-e077-ea37-414e0eab5c3a.png)
|:-:|

LINEきましたね。
大丈夫そうなので少し戻り、明日のスケジュールを送るメイン処理も追加しておきます。

```js:main.gs
function sendTomorrowSchedule() {
  var accessToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  // getMessage()の引数を1にするだけです。
  // 明後日なら3、1週間後なら7にすれば該当するイベント情報を取得できます。
  var message = getMessage(1);
  var options =
   {
     'method'  : 'post'
    ,'payload' : 'message=' + message
    ,'headers' : {'Authorization' : 'Bearer '+ accessToken}
    ,muteHttpExceptions:true
   };
  UrlFetchApp.fetch('https://notify-api.line.me/api/notify',options);
}
```

以上でコーディングが完了です。

#自動送信の設定
これまた便利な機能。

①編集 > ②現在のプロジェクトのトリガー

|![q_s_17.png](https://qiita-image-store.s3.amazonaws.com/0/394580/bd19435d-9902-6362-ac13-1ec3da6fe8fa.png)
|:-:|

③新しいトリガーを作成します

|![q_s_18.png](https://qiita-image-store.s3.amazonaws.com/0/394580/a41e7444-c068-9bed-10c5-2c42a0e89f3e.png)
|:-:|

④実行する関数を選択：sendTodaySchedule
⑤イベントのソースを選択：時間主導型
⑥時間ベースのトリガーのタイプを選択：日付ベースのタイマー
⑦時刻を選択：午前５時～午前６時
を設定 > ⑧保存

|![q_s_19.png](https://qiita-image-store.s3.amazonaws.com/0/394580/35813f2f-96ee-6b12-0ee0-1cbf52105f7f.png)
|:-:|

これで今日の予定を送信するトリガーは完了です。
次に、同じ要領で明日の予定を送信するトリガーを設定していきます。
画面右下にある ⑨トリガーを追加 > 関数(sendTomorrowSchedule)と時間を設定 > 保存

|![q_s_20.png](https://qiita-image-store.s3.amazonaws.com/0/394580/709a5543-cee9-3fc4-738b-e94927343e9a.png)
|:-:|

|![q_s_21.png](https://qiita-image-store.s3.amazonaws.com/0/394580/7de1ae36-9b56-dc8b-1522-276bc731ba3d.png)
|:-:|

以上で自動送信の設定が完了です。

#まとめ
<details><summary>コード</summary><div>

```js:main.gs
/**
* Googleカレンダーの
* 今日の予定をLINEに送る
*/
function sendTodaySchedule() {
  var accessToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  var message = getMessage(0);
  var options =
   {
     'method'  : 'post'
    ,'payload' : 'message=' + message
    ,'headers' : {'Authorization' : 'Bearer '+ accessToken}
    ,muteHttpExceptions:true
   };
  UrlFetchApp.fetch('https://notify-api.line.me/api/notify',options);
}

/**
*  Googleカレンダーの
* 明日の予定をLINEに送る
*/
function sendTomorrowSchedule() {
  var accessToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  var message = getMessage(1);
  var options =
   {
     'method'  : 'post'
    ,'payload' : 'message=' + message
    ,'headers' : {'Authorization' : 'Bearer '+ accessToken}
    ,muteHttpExceptions:true
   };
  UrlFetchApp.fetch('https://notify-api.line.me/api/notify',options);
}

/**
* メッセージ内容取得
* @param {number} 今日起算の日数
* @return {string} メッセージ内容
*/
function getMessage(prm) {
  const week = ['日','月','火','水','木','金','土'];
  var cal = CalendarApp.getCalendarById('*************@gmail.com');
  var date = new Date();
  var strBody = '';
  var strHeader = '';
  // タイトル
  if ( prm==0 ) {
    strHeader = '今日 ';
  } else if ( prm==1 ) {
    strHeader = '明日 ';
  } 
  date = new Date(date.getYear(),date.getMonth(),date.getDate() + prm);
  strHeader += Utilities.formatDate(date,'JST','yyyy/M/d')
                + '(' +week[date.getDay()] + ') の予定\n';
  // 内容
  strBody = getEvents(cal,date);
  if ( _isNull(strBody) ) strBody = '予定はありません。';
  return strHeader + strBody;
}

/**
* イベント取得
* @param {object} カレンダー
* @param {date} 取得日
* @return {string} イベント情報
*/
function getEvents(prmCal,prmDate) {
  var strEvents = '';
  var strStart = '';
  var strEnd = '';
  var strTime = '';
  var strLocation = '';
  var strDescription = '';
  if ( !_isNull(prmCal) ) {
     var arrEvents = prmCal.getEventsForDay(new Date(prmDate));
     for (var i=0; i<arrEvents.length; i++) {
       if ( !_isNull(strEvents) ) strEvents += '\n';
       strStart = _HHmm(arrEvents[i].getStartTime());
       strEnd = _HHmm(arrEvents[i].getEndTime());
       if ( strStart===strEnd ) {
         strTime = '終日';
       } else {
         strTime = strStart + '～' + strEnd;
       }
       strEvents += '･' + strTime + '【' + arrEvents[i].getTitle() + '】';
       strLocation = arrEvents[i].getLocation();
       strDescription = arrEvents[i].getDescription();
       if ( !_isNull(strLocation) ) strEvents += '\n　場所：' + strLocation;
       if ( !_isNull(strDescription) ) strEvents += '\n　説明：' + strDescription;
     }
  }
  return strEvents;
}

/**
* 時間フォーマット
*/
function _HHmm(str){
  return Utilities.formatDate(str,'JST','HH:mm');
}

/**
* NULL判定
*/
function _isNull(prm) {
  if ( prm=='' || prm===null || prm===undefined ) {
    return true;
  } else {
    return false;
  }
}
```

</div></details>

入門ということでコードは単純です。

最近すっかりGASにハマっているので、ちまちまと遊んだものを書いていければと思います。
次は個人的に便利につかっている、天気予報を~~また~~LINEに送るやつを作ります。

余談ですが、時刻きっかりに送りたい場合は[こちら](https://qiita.com/sumi-engraphia/items/465dd027e17f44da4d6a)がとても参考になります。

#参考サイト
[Google Apps ScriptからLINE NotifyでLINEにメッセージを送る - Qiita](https://qiita.com/tadaken3/items/5f916a12587e42ece814)
[Google Apps Script（GAS）から LINE notify API を利用して通知を送る - Qiita](https://qiita.com/yasushiito/items/dfa80dd934e44231f294)
[GASでGoogleカレンダーの当日の情報を定期的にGmailに送信する！ - IT-LIFEブログ](https://nok-lab-life.hatenablog.com/entry/2018/08/25/131545)
[曜日を一行で取得するJavaScriptはこちらです。 _ Ginpen.com](https://ginpen.com/2015/08/03/one-liner-weekday-js/)
[【GAS】getEventsメソッドを完全マスター](https://google-apps-script.net/instructions/?p=489)

ありがとうございました。

[^1]: ![q_s_line.png](https://qiita-image-store.s3.amazonaws.com/0/394580/a31f359b-1a86-7c38-e422-e417c6de8a06.png)

# 修正（最新：19/10/27）
 - `getMessage()` を修正いたしました。（19/10/27）
 <br>【誤】`weekw[date.getDay()]` 
 <br> 【正】`week[date.getDay()]`