Googleカレンダーから予定を取得し毎朝GoogleChatに投稿するGAS

GoogleAppsScriptを用いてGoogleカレンダーから予定を取得し毎朝GoogleChatに当日の予定を投稿する方法を紹介します。

概要

Googleカレンダーから当日の予定を取得し、Google ChatのWebhookを利用して特定のルーム・スレッドに投稿するGASを紹介したいと思います。

下記の流れで紹介していきます。

  1. Google Chatについて
  2. Webhookの準備
  3. 投稿内容の作成・投稿
  4. 投稿スレッドの指定(チャットルームの場合)
  5. トリガーの設定

1. Google Chatについて

Google ChatはGoogleの提供しているチャットサービスで、1対1の個別チャットと複数人のグループチャットが用意されています。

また、Google WorkspaceではWebhookを利用することも可能です。
このGoogle ChatのWebhookを用いると、とても簡単に特定のチャットルームへBotのような定期投稿が可能になります。

今回はこのGoogle ChatのWebhookを用いて、当日の予定を指定のチャットルーム・スレッドに投稿するGASを紹介したいと思います。

2.Webhookの準備

作業を行うGoogle WorkspaceのアカウントでWebhookの設定ができるようになっている必要があります。
まずは、現在Webhookの設定が可能かどうかの確認をしましょう。

Webhookの確認

(1) Google Chatの適当なチャットルームを開き、ルーム名のメニューを確認します。

(2) メニューに「Webhookを管理」の項目が表示されていれば大丈夫です。

もしも表示されていない場合は、公式ヘルプの「ユーザーが Chat で Webhook を追加して使用できるようにする」を参考に、Workspace管理者から設定をしてもらってください。

Webhookの作成

Webhookが設定可能な状態になったら早速Webhookを作成したいと思います。

(1) 先ほどの「Webhookを管理」を選択、任意の名前とアイコンを設定し保存します。(アイコンは省略可)

(2) Webhookが作成されました。

※もし既存のWebhookがあるチャットルームの場合は、この「別のWebhookを追加」から追加してください。

(3) ここでコピーできるURLを以降のGASで使用するので、コピーしておいてください。

3. 投稿内容の作成・投稿

GoogleChatのWebhookが準備できたのでGASを作成していきたいと思います。
カレンダーから予定を取得する部分は以前紹介した予定を取得してメール送信する記事を流用します。

変更内容:
・関数名の変更
・メール宛先と件名の削除
・メール送信処理の削除

その他に関しては元の記事のままです、細部の解説もそちらをご参照ください。

また、動作確認用にGoogleカレンダーにダミーの予定等を追加しておくことを忘れないように気を付けてください。

function sendEventsChat() {

  const calendarId = 'sample.calendar@example.com';
  const calendar = CalendarApp.getCalendarById(calendarId);

  const today = new Date();
  const events = calendar.getEventsForDay(today);

  if (events.length === 0) {
    console.log("No events found.");
  }
  else {
    console.log("Events:");

    for (event in events) {

      let title = events[event].getTitle();

      let startHour = events[event].getStartTime().getHours();
      let startMinute = events[event].getStartTime().getMinutes();
      let endHour = events[event].getEndTime().getHours();
      let endMinute = events[event].getEndTime().getMinutes();

      events[event].eventStr = title + '(' + startHour + '時' + startMinute + '分~' + endHour + '時' + endMinute + '分)';

      console.log(events[event].eventStr);
    }

    let todayDayStr = '';

    switch(today.getDay()){
      case 0: todayDayStr = '日'; break;
      case 1: todayDayStr = '月'; break;
      case 2: todayDayStr = '火'; break;
      case 3: todayDayStr = '水'; break;
      case 4: todayDayStr = '木'; break;
      case 5: todayDayStr = '金'; break;
      case 6: todayDayStr = '土'; break;
    }

    let todayStr = String(today.getMonth() + 1) + '月' + today.getDate() + '日(' + todayDayStr + ')';
    
    // ここから変更部
    // 投稿文テンプレート
    const bodyTemplate = `{todayStr}の予定をお知らせします。

{events}

以上
`;
    // 予定の文字列処理 ※詳細は元記事を参照
    let eventsStr = events.map(e => e.eventStr).join('\n');

    // 投稿文を置き換え
    let body = bodyTemplate
        .replace('{todayStr}', todayStr)
        .replace('{events}', eventsStr);

    // 変更部ここまで
  }
}

これにGoogleChatのWebhookで投稿する処理を追加します。

function sendEventsChat() {

  const calendarId = 'sample.calendar@example.com';
  const calendar = CalendarApp.getCalendarById(calendarId);

  const today = new Date();
  const events = calendar.getEventsForDay(today);

  if (events.length === 0) {
    console.log("No events found.");
  }
  else {
    console.log("Events:");

    for (event in events) {

      let title = events[event].getTitle();

      let startHour = events[event].getStartTime().getHours();
      let startMinute = events[event].getStartTime().getMinutes();
      let endHour = events[event].getEndTime().getHours();
      let endMinute = events[event].getEndTime().getMinutes();

      events[event].eventStr = title + '(' + startHour + '時' + startMinute + '分~' + endHour + '時' + endMinute + '分)';

      console.log(events[event].eventStr);
    }

    let todayDayStr = '';

    switch(today.getDay()){
      case 0: todayDayStr = '日'; break;
      case 1: todayDayStr = '月'; break;
      case 2: todayDayStr = '火'; break;
      case 3: todayDayStr = '水'; break;
      case 4: todayDayStr = '木'; break;
      case 5: todayDayStr = '金'; break;
      case 6: todayDayStr = '土'; break;
    }

    let todayStr = String(today.getMonth() + 1) + '月' + today.getDate() + '日(' + todayDayStr + ')';
    
    const bodyTemplate = `{todayStr}の予定をお知らせします。

{events}

以上
`;

    let eventsStr = events.map(e => e.eventStr).join('\n');

    let body = bodyTemplate
        .replace('{todayStr}', todayStr)
        .replace('{events}', eventsStr);

    // ここから追加部
    // 投稿文をJSONデータの形式に整える
    let message = {
      'text': body
    };

    // WebhookのURL(前項の(3)でコピーしたものに置き換えてください)
    const webhookUrl = 'https://chat.googleapis.com/v1/spaces/XXXXXXXXXXX/messages?key=(Webhook用Key)token=(Webhook用Token)';

    // オプションパラメータを設定
    let options = {
    'payload' : JSON.stringify(message),
    'myamethod' : 'POST',
    'contentType' : 'application/json'
    };

    // WebhookのURL対してHTTP POSTを実行
    UrlFetchApp.fetch(webhookUrl, options);

    // 追加部ここまで
  }
}

HTTP POSTとは:

HTTP POSTはHTTP GETと共に広く用いられているURLを用いたリクエストのひとつです。
HTTP POSTとHTTP GETの違いはリクエスト時にデータを付与できるか否かという部分です。
今回のGoogle ChatのWebhookは、投稿文のデータを持ったHTTP POSTを用いた投稿リクエストということになります。
GASではUrlFetchApp.fetch(url, options)を用いてこのHTTP POSTを実行することができます。

実行結果(投稿内容の作成・投稿)

実行結果として、Webhookを作成したチャットルームに当日の予定が投稿されます。

4. 投稿スレッドの指定(チャットルームの場合)

1対1の「チャット」の場合、全ての投稿は1つのスレッドに投稿されるので問題ありません。
ですが、複数人が参加する「チャットルーム」の場合、投稿はスレッド毎にまとめられます。

ここまでに作ったGASでは投稿するチャットルームの指定はURLでされているものの、スレッドの指定はされていません。
そのためチャットルームで動作させると実行するたびに画像のように新しいスレッドが作成されてしまいます。

投稿するスレッドを指定して投稿するよう、オプションを追加したいと思います。

まずはスレッド作成用の投稿をし、スレッドの情報を取得します。
新しい関数createNewsThread()を作成します。

function createNewsThread() {

  // WebhookのURL(置き換えてください)
  const webhookUrl = 'https://chat.googleapis.com/v1/spaces/XXXXXXXXXXX/messages?key=(Webhook用Key)token=(Webhook用Token)';

  // スレッドの最初の投稿文
  let body = {
  'text' : '【本日の予定リマインド】スレッド'
  };
  
  let options = {
  'payload' : JSON.stringify(body),
  'myamethod' : 'POST',
  'contentType' : 'application/json'
  };

  // WebhookのURL対してHTTP POSTを実行
  let response = UrlFetchApp.fetch(webhookUrl, options);
  // 実行ログに投稿先スレッド情報を含むデータを表示
  Logger.log(response);
}

実行結果(投稿スレッドの指定)

Google Chatに予定リマインド用のスレッドが作成されました。

また実行結果として、GASの実行ログに投稿のデータが表示されます。
このうちのthreadの情報を用いて、投稿先のスレッドを指定します。

スレッド情報の反映

先ほど実行ログに表示されたスレッド情報を含めて投稿することで、同じスレッドを指定して投稿することができます。

function sendEventsChat() {

  const calendarId = 'sample.calendar@example.com';
  const calendar = CalendarApp.getCalendarById(calendarId);

  const today = new Date();
  const events = calendar.getEventsForDay(today);

  if (events.length === 0) {
    console.log("No events found.");
  }
  else {
    console.log("Events:");

    for (event in events) {

      let title = events[event].getTitle();

      let startHour = events[event].getStartTime().getHours();
      let startMinute = events[event].getStartTime().getMinutes();
      let endHour = events[event].getEndTime().getHours();
      let endMinute = events[event].getEndTime().getMinutes();

      events[event].eventStr = title + '(' + startHour + '時' + startMinute + '分~' + endHour + '時' + endMinute + '分)';

      console.log(events[event].eventStr);
    }

    let todayDayStr = '';

    switch(today.getDay()){
      case 0: todayDayStr = '日'; break;
      case 1: todayDayStr = '月'; break;
      case 2: todayDayStr = '火'; break;
      case 3: todayDayStr = '水'; break;
      case 4: todayDayStr = '木'; break;
      case 5: todayDayStr = '金'; break;
      case 6: todayDayStr = '土'; break;
    }

    let todayStr = String(today.getMonth() + 1) + '月' + today.getDate() + '日(' + todayDayStr + ')';
    
    const bodyTemplate = `{todayStr}の予定をお知らせします。

{events}

以上
`;

    let eventsStr = events.map(e => e.eventStr).join('\n');

    let body = bodyTemplate
        .replace('{todayStr}', todayStr)
        .replace('{events}', eventsStr);

    // ここから追加部
    // スレッド情報追加(取得したthread情報に置き換えてください)
    let message = {
      'text': body,
      'thread': {
        "name": "spaces/AAAAAAAAAAA/threads/AAAAAAAAAAA"
      }
    };
    // 追加部ここまで

    const webhookUrl = 'https://chat.googleapis.com/v1/spaces/XXXXXXXXXXX/messages?key=(Webhook用Key)token=(Webhook用Token)';

    let options = {
    'payload' : JSON.stringify(message),
    'myamethod' : 'POST',
    'contentType' : 'application/json'
    };

    UrlFetchApp.fetch(webhookUrl, options);
  }
}

実行結果(スレッド指定)

先ほどのスレッドに予定の投稿ができました。

5. トリガーの設定

最後にこのコードを毎朝実行するトリガーを設定します。

始業時刻前に当日の予定をリマインドすることを想定し、午前8時~9時の間に実行するようにします。
トリガーの設定方法についてはトリガーの紹介記事をご参照ください。

設定したトリガー:

まとめ

今回はGoogleカレンダーから当日の予定を取得し、GoogleChatのWebhookを利用して投稿するGASを紹介しました。

GoogleChatのWebhookはGoogleWorkspaceで提供されている便利な機能のひとつです。
予定のリマインド以外にも、フォームに回答があったらChatに通知したり、未読メールが一定数溜まったらアラートを投稿したりと使いどころが色々とありそうです。
ぜひ活用してみてください。

Google Cloudの導入は当社にご相談ください

ITディストリビューターであるTD SYNNEXはGoogle Cloud™ Partner Award を受賞するなど、長年にわたりGoogle™のグローバル認定ディストリビューターとして、総合的な Googleソリューションを提供しています。お客様にとって最適なソリューションの提案や導入、活用をサポートします。

製品・サービスについてのお問合せ

情報収集中の方へ

導入事例やソリューションをまとめた資料をご提供しております。

資料ダウンロード
導入をご検討中の方へ

折り返し詳細のご案内を差し上げます。お問い合わせお待ちしております。

お問い合わせ