【GAS】google app scriptでgoogleカレンダーの明日の予定をLINE botへ送信!

【GAS】google app script!

googleカレンダーを前日に送って、スケジュールを見落とすことが無いようにしたい!

たったか
たったか

やってしまった。これはやばい。やばいやばいばやばやういばう…

魔理沙
魔理沙

いきなりどうしたんだぜ?休みが無さすぎてついに頭まで壊れちゃったのかだぜ?

たったか
たったか

スケジュール管理ミスでさ、仕事で大ポカやらかしてしまって凹んでるんです。話しかけないでくださいまじで。

霊夢
霊夢

googleカレンダーにはちゃんと予定書いてたんでしょ?何で見ないの?

たったか
たったか

(理由がわかれば苦労しないぜ)

だったらあれ作ればいいじゃないか!LINEボット?あれ、簡単なんだろ?

霊夢
霊夢

そうよね、googleカレンダーからLINEに明日の予定が送れれば前日の準備もできるし、一度設定してしまえばずっと自動で使えるんでしょ?作っちゃいなさいよ!

たったか
たったか

昨日寝てないから眠い…

たったか
たったか

よしッ!つくるぞッ!(唐突)

ということで、今回は本当に大ポカをやらかしてしまってどうにかならないかと考えた結果、googleカレンダーからLINEに予定を投げるスクリプトをGASでやってみようと思います!

あのね、まじでね、辛かったの。これを見ているあなた、もしかしたら一度痛い目に遇ってたりしない?そんな人は是非チャレンジしてみて!もうあんな吐き気がする一日はこりごりだよ…。

GASのコード、コピペして使ってもいいですが、ちょっと解るといろいろ使い回せるよ!

ってなことで、とりあえず全体的なコードを最初に貼っておきます!とりあえず動けば良い!って人はとりあえず試してみてね!

チャンネルアクセストークン、ユーザーID、カレンダーIDは取得しておいてね!(LINEボットの作成は別に書きます。)

// Googleカレンダーから明日の予定を取得し、LINEに送信する関数
function sendScheduleToLine() {
  // 必要な情報を定義
  const CHANNEL_ACCESS_TOKEN = 'チャンネルアクセストークン';
  const USER_ID = 'ユーザーID';
  const CALENDAR_ID = 'カレンダーID';

  // 明日の日付と曜日を取得
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  const dayOfWeek = ['日', '月', '火', '水', '木', '金', '土'][tomorrow.getDay()];

  // 明日の予定を取得
  const calendar = CalendarApp.getCalendarById(CALENDAR_ID);
  const events = calendar.getEventsForDay(tomorrow);

  // 送信するメッセージを作成
  let message = '明日(${(tomorrow.getMonth() + 1)}月${tomorrow.getDate()}日${dayOfWeek}曜日)の予定はこちらです。\n\n';

  // 予定がない場合
  if (events.length === 0) {
    message += '予定はありません。\n\n';
  } else {
    // 予定がある場合、それぞれの予定についてタイトル・時間・場所を追加
    events.forEach(event => {
      const startTime = event.getStartTime();
      const endTime = event.getEndTime();
      const title = event.getTitle();
      const location = event.getLocation() || '場所未定';

      message += '【タイトル】${title}\n';
      message += '【時間】${startTime.getHours()}:${String(startTime.getMinutes()).padStart(2, '0')} ~ ${endTime.getHours()}:${String(endTime.getMinutes()).padStart(2, '0')}\n';
      message += '【場所】${location}\n\n';
    });
  }

  // LINEに送信
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ${CHANNEL_ACCESS_TOKEN}',
    },
    payload: JSON.stringify({
      to: USER_ID,
      messages: [
        {
          type: 'text',
          text: message,
        },
      ],
    }),
  });
}

// トリガーを作成する関数
function createTimeDrivenTriggers() {
  // 既存のトリガーを削除
  const triggers = ScriptApp.getProjectTriggers();
  for (const trigger of triggers) {
    ScriptApp.deleteTrigger(trigger);
  }

  // 新しいトリガーを作成(毎日20時に実行)
  ScriptApp.newTrigger('sendScheduleToLine')
    .timeBased()
    .atHour(20)
    .everyDays(1)
    .create();
}

必要な情報を定義する

// 必要な情報を定義 という部分では、このスクリプトで使用する情報を定数として定義しています。具体的には以下の3つの情報を定義しています。

  // 必要な情報を定義
  const CHANNEL_ACCESS_TOKEN = 'チャンネルアクセストークン';
  const USER_ID = 'ユーザーID';
  const CALENDAR_ID = 'カレンダーID';
  1. CHANNEL_ACCESS_TOKEN:LINE Messaging APIを利用するために必要なチャンネルアクセストークンです。このトークンは、LINE Developersサイトから取得できます。ダミーの値を入れてありますが、実際に利用する際には、取得したアクセストークンに置き換えてください。
  2. USER_ID:LINEボットにメッセージを送信する際の宛先となるユーザーIDです。これもLINE Developersサイトから取得できます。現在はダミーの値が入っていますので、実際に利用する際には、取得したユーザーIDに置き換えてください。
  3. CALENDAR_ID:Googleカレンダーの識別子です。Googleカレンダーから予定を取得する際に必要になります。ダミーの値が入っていますので、実際に利用する際には、自分のGoogleカレンダーIDに置き換えてください。

カレンダーIDは、googleアカウントの特にややこしい設定をしていなければ、googleカレンダーと紐づいているメールアドレスです!

このように、この部分ではスクリプトで使用する情報を定義しているだけで、実際の処理は行っていません。後続の処理でこれらの情報を利用します。

明日の日付と曜日を取得する

  // 明日の日付と曜日を取得
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  const dayOfWeek = ['日', '月', '火', '水', '木', '金', '土'][tomorrow.getDay()];
  1. まず、現在の日付と時刻を取得して、tomorrowという変数に格納しています。この段階では、tomorrowには現在の日付が入っています。
  2. 次に、tomorrowの日付に1日加算して、実際に明日の日付を取得します。
  3. さらに、tomorrowの曜日を取得します。getDay()メソッドは0(日曜日)から6(土曜日)までの整数を返すため、それを日本語の曜日文字列に変換するために配列を利用しています。

この部分の処理が完了すると、tomorrowには明日の日付が格納され、dayOfWeekには明日の曜日(日本語表記)が格納されます。これらの情報は、後続の処理でメッセージに追加するために使用されます。

明日の予定を取得する

この部分では、Googleカレンダーから明日の予定を取得しています。具体的な処理手順は以下の通り。

  // 明日の予定を取得
  const calendar = CalendarApp.getCalendarById(CALENDAR_ID);
  const events = calendar.getEventsForDay(tomorrow);
  1. まず、CalendarApp.getCalendarById()関数を使用して、指定されたカレンダーIDのカレンダーを取得し、calendarという変数に格納しています。
  2. 次に、calendar.getEventsForDay()関数を使用して、先ほど取得した明日の日付(tomorrow)に該当する予定を全て取得します。取得した予定は、eventsという変数に格納されます。

この部分の処理が完了すると、eventsという変数には、明日の予定が格納されます。後続の処理で、これらの予定をメッセージに追加していきます。

霊夢
霊夢

ちょっと質問!格納する場合はletじゃなくてconstの方が良いの?

constletはどちらも変数を宣言するためのキーワードですが、使い分けがあります。

constは、宣言時に値が割り当てられ、その後変更されない(定数として扱われる)変数を定義する際に使用します。constで定義された変数は、再代入できません。

一方、letは、宣言時に値が割り当てられるかもしれないし、その後変更されるかもしれない(可変な)変数を定義する際に使用します。letで定義された変数は、後から再代入できます。

先程のコードでは、constを使用している変数は、宣言後に変更されない値を保持するために使用されています。例えば、CHANNEL_ACCESS_TOKENUSER_IDCALENDAR_IDなどは、スクリプトの実行中に変更されない値です。

一方、letを使用している変数(message)は、後で値が変更される可能性があるため、letを使って宣言されています。

たったか
たったか

要するに、変数の値が宣言後に変更されることがない場合は constを、変更される可能性がある場合はletを使うと、コードが分かりやすくなるわけだね。また、constを使用することで、誤って変数の値が書き換えられることを防ぐことができるよ!覚えておくと便利だね!

ちなみに、次の部分でletを使うので、じっくり追ってみるとわかりやすいと思いますよ!

予定がある場合とない場合でメッセージを作成する

この部分では、先ほど取得した明日の予定(events)がある場合とない場合で、送信するメッセージの内容を作成しています。具体的な処理手順はこちら。

  // 送信するメッセージを作成
  let message = '明日(${(tomorrow.getMonth() + 1)}月${tomorrow.getDate()}日${dayOfWeek}曜日)の予定はこちらです。\n\n';

  // 予定がない場合
  if (events.length === 0) {
    message += '予定はありません。\n\n';
  } else {
    // 予定がある場合、それぞれの予定についてタイトル・時間・場所を追加
    events.forEach(event => {
      const startTime = event.getStartTime();
      const endTime = event.getEndTime();
      const title = event.getTitle();
      const location = event.getLocation() || '場所未定';

      message += '【タイトル】${title}\n';
      message += '【時間】${startTime.getHours()}:${String(startTime.getMinutes()).padStart(2, '0')} ~ ${endTime.getHours()}:${String(endTime.getMinutes()).padStart(2, '0')}\n';
      message += '【場所】${location}\n\n';
    });
  }
  1. let message = '';という行で、空の文字列を持つmessageという変数を宣言しています。この変数に、後続の処理でメッセージの内容を追加していきます。
  2. 次に、events.lengthが0より大きいかどうかで分岐しています。events.lengthが0より大きい場合は、明日の予定が存在することを意味します。
  3. 予定が存在する場合は、まずmessageに「明日の予定はこちらです」という文章と日付、曜日を追加しています。
  4. さらに、取得した予定(events)を1つずつループ処理で処理し、各予定のタイトル、時間、場所をメッセージに追加しています。
  5. 一方、events.lengthが0の場合、つまり予定が存在しない場合は、elseブロック内でメッセージに「明日の予定はありません」という文章と日付、曜日を追加しています。

この部分の処理が完了すると、messageには送信する内容が格納されています。後続の処理で、このメッセージをLINEに送信します。

魔理沙
魔理沙

events.lengthの .length って何なのかだぜ?

events.length.lengthは、配列の長さ(要素数)を取得するためのプロパティです。つまり、events配列に含まれる要素(ここでは明日の予定)の数を表しています。

例えば、events配列に3つの予定が格納されている場合、events.lengthは3を返します。もし予定が1つもない場合、events.lengthは0を返します。

たったか
たったか

このコードでは、events.length > 0という条件式を使って、予定が存在するかどうかを判断してるのね。events.lengthが0より大きい場合、明日の予定があると判断したら、それに応じたメッセージを作成するってわけ。逆に、events.lengthが0の場合、明日の予定がないと判断して、その旨のメッセージを作ってるんだよ!

霊夢
霊夢

なるほど!それでイベントがあるかないかを判別してるのね!

たったか
たったか

そーゆーことだねー!

LINEにメッセージを送信する

この部分では、先ほど作成したメッセージ(message)をLINEのボットに送信しています。ここはちょっとむずかしいので例を作って説明してみましたので、コードを見ながら進めていきましょう。

  // LINEに送信
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ${CHANNEL_ACCESS_TOKEN}',
    },
    payload: JSON.stringify({
      to: USER_ID,
      messages: [
        {
          type: 'text',
          text: message,
        },
      ],
    }),
  });
  1. まず、UrlFetchApp.fetch() 関数を使用して、LINE Messaging APIのエンドポイントにPOSTリクエストを送信します。リクエストのURLは https://api.line.me/v2/bot/message/pushです。
  2. UrlFetchApp.fetch() 関数の第2引数として、リクエストに必要な設定をオブジェクト形式で渡しています。具体的には以下の設定を指定しています。
    • method: リクエストのHTTPメソッド(ここではPOST)
    • headers: リクエストヘッダーに設定する値。Authorizationには、BearerCHANNEL_ACCESS_TOKENを組み合わせた形式でアクセストークンを設定しています。Content-Typeには、JSON形式のデータを送信することを示すapplication/jsonを設定しています。
    • payload: リクエストの本体(ペイロード)となるデータ。JSON.stringify()を使って、オブジェクトをJSON文字列に変換しています。このオブジェクトには、to(送信先のユーザーID)とmessages(送信するメッセージ)が含まれています。messagesは、配列形式で指定され、その中にオブジェクトが1つ含まれています。このオブジェクトには、type(メッセージタイプ)とtext(メッセージ本文)が設定されています。

この部分の処理が完了すると、作成したメッセージがLINEのボットに送信されます。

霊夢
霊夢

……。

魔理沙
魔理沙

はぁ?

たったか
たったか

わかる。その反応。ここ何言ってるかわからないよね?んじゃちょっと例え話しよっか。

UrlFetchApp.fetch って何なのよ?

ここでは、UrlFetchApp.fetch()関数を使って、LINE Messaging APIに郵便物(リクエスト)を送ることを想像してみてください。その際、郵便物を送るには、送る方法(method)、宛先や差出人などの情報(headers)、そして郵便物の中身(payload)が必要になります。

  • method: 郵便物を送る方法です。ここでは、POST(速達郵便のようなもの)を指定しています。
  • headers: 郵便物に記載する情報です。
    • Authorizationは、差出人が正しい人物であることを証明するための証明書のようなものです。
    • Content-Typeは、郵便物の中身がどのような形式であるかを示す情報です(例:手紙、小包など)。ここでは、JSON形式のデータであることを示しています。
  • payload: 郵便物の中身です。手紙や小包の中に入れるものを指定します。ここでは、JSON.stringify()を使って、オブジェクト(郵便物の中身)をJSON文字列に変換しています。このオブジェクトには、送信先のユーザーID(宛先)と、送信するメッセージ(手紙の内容)が含まれています。

このように考えると、UrlFetchApp.fetch()関数を使って、郵便物(リクエスト)をLINE Messaging APIに送るために、送る方法(method)、宛先や差出人などの情報(headers)、そして郵便物の中身(payload)を指定していることがわかるとおもいます。わかって。

たったか
たったか

つまり、この郵便物が正しく送られると、作成したメッセージがLINEのボットに配信されるってわけ。

トリガーを作成する関数を作って自動化する

ついにここがキモ!自動化するところですね!

// トリガーを作成する関数
function createTimeDrivenTriggers() {
  // 既存のトリガーを削除
  const triggers = ScriptApp.getProjectTriggers();
  for (const trigger of triggers) {
    ScriptApp.deleteTrigger(trigger);
  }

  // 新しいトリガーを作成(毎日20時に実行)
  ScriptApp.newTrigger('sendScheduleToLine')
    .timeBased()
    .atHour(20)
    .everyDays(1)
    .create();
}
  1. 「// 既存のトリガーを削除」の部分のコードで既存のトリガーをすべて削除します。これにより、重複したトリガーが作成されることを防ぎます。
  2. 「// 新しいトリガーを作成(毎日20時に実行)」の部分のコードで新しいトリガーを作成します。このトリガーは、毎日20時に sendScheduleToLine 関数を実行するよう設定されています。

ScriptApp.newTrigger('sendDailySchedule'): sendDailyScheduleは、関数を実行するトリガーを作成する準備をします。

  • .timeBased():トリガーのタイプを「時間ベース」に設定します。これにより、特定の時間や間隔でスクリプトを実行できるようになります。
  • .atHour(20):トリガーを毎日20時に実行するように設定します。この場合、作成したトリガーは毎日sendDailySchedule関数を実行します。
  • .everyDays(1):トリガーを作成する際に、実行間隔を毎日に設定するためのものです。
    • .everyDays():このメソッドを使用して、トリガーの実行間隔を「日単位」で設定します。
    • (1):この引数は、実行間隔の日数を指定します。この場合、1 を指定しているため、毎日実行されることになります。
  • .create():設定した内容でトリガーを作成します。

この関数を実行することで、毎日20時にsendScheduleToLine関数が自動的に実行されるトリガーが作成されます。これにより、毎日Googleカレンダーから明日の予定を取得し、LINEのボットに送信することができます。

スクリプトを書いただけでは勝手に実行してくれないのでご注意を!(惨敗)

【超重要!】トリガーを実行してトリガーを実際に生成する

魔理沙
魔理沙

動かないんだぜ~~~~~~!

たったか
たったか

これはまじでハマった。初めてスクリプト動かす時にアカウントを選ぶタイミングでいつもと違うものが表示される時があって、それかな?と思ったけど、解決策がわかったよ!

Google Apps Scriptでは、スクリプトを定期的に実行したい場合、まずトリガーを作成する必要があります。そしてcreateTimeDrivenTriggers関数を実行することで、定期的に実行されるべき関数(この場合はsendScheduleToLine関数)に対応するトリガーが作成されます。

トリガーを作成した後、設定したタイミングでスクリプトが自動的に実行されるようになります。この例では、createTimeDrivenTriggers関数を実行することで、5分ごとや毎日20時にsendScheduleToLine関数が実行されるトリガーが作成されます。

新しいプロジェクトを開始する際や、トリガーの設定を変更したい場合は、まずcreateTimeDrivenTriggers関数を実行してトリガーを設定してください。その後、トリガーによってスクリプトが定期的に実行されることを確認できます。

霊夢
霊夢

これで毎日指定した時間にgoogleカレンダーに登録した予定がLINEボットに送られて、スマホでもお知らせしてくれるようになったのね!次からスケジュール管理ミスなんて理由にもならない失敗しないでよね!

たったか
たったか

実際、好きなキャラのボット作って毎日お知らせしてくれるからちゃんと確認するようになるはず!これはかなり自分自身効果てきめん何なのでは?と期待しております!

自動化して、日々のタスク管理や業務を楽ちんにしよう!

一番上に書いてあるスクリプトを使用することで、Googleカレンダーから明日の予定を取得し、毎日20時にLINEのボットに送信する機能が実現できます。また、トリガーを作成する関数を使って、自動的にスクリプトが実行されるように設定することができます。

他にもまだまだGASには使い道があるので、というかむしろこんなの序の口レベルなのです!こういった自動化は大人数の組織ほど効果は絶大なので、忘れやすい人がいるなら作ってみるのも良いかもしれないですね!

はい、僕のことです!