marukot-chの日記

弱小SEの雑記です

Googleカレンダーの予定をチャットツールに通知する(共通編)

目的

  • Discordを最強の情報収集ツールにする256の方法(その1)などを読んでいる人がいれば、Discordにどんな情報でも保存しておきたいと思っているはず

marukot-ch.hatenablog.com

目次

作業内容

GoogleカレンダーのカレンダーIDを取得

  • Googleカレンダーを開きます
  • 左にあるマイカレンダーから、今回通知したいカレンダーのメニュー(...)をクリックしてください
  • 設定と共有をクリックしてください
  • 下のほうにある「カレンダー ID」をコピーしてください
    • アカウントのデフォルトカレンダーの場合、「xxx@gmail.com」などになります
    • 追加したカレンダーの場合、「xxx@group.calendar.google.com」などになります
      • あとで使うので、どこかにメモしておいてください
  • 上記文章でわかりづらい場合は、以下記事でやり方を確認してください

【Googleカレンダー】カレンダーIDを確認する方法|共有やAPIで利用

Google Apps Scriptプロジェクトの作成

  • Google Apps Scriptを開きます
    • ログインしていない場合、Googleアカウントにログインしてください。その後、再度Google Apps Scriptを開いてください
  • 左の「新しいプロジェクト」をクリックし、無題のGoogle Apps Scriptを作成してください

左の「新しいプロジェクト」をクリックして、Google Apps Scriptプロジェクトを作成

  • 「無題のプロジェクト」をクリックし、好きな名前をつけます(NotifyTaskDiscordなど)

無題のプロジェクトに名前をつける

  • 右に書いてあるコードを全部削除します
コードの貼り付け
  • 以下のGoogle Apps Scriptコードを貼り付けてください
// author: marukot-ch
// version: 1.0
// 左の歯車マークを押下し、下にある「スクリプト プロパティ」で「追加」を押下し、
// プロパティ名(CALENDAR_IDなど)と値を設定してください。Discord、Slackどちらかで大丈夫です。
// ---- 必須 ----
// GoogleカレンダーのID名(通常はメールアドレス): CALENDAR_ID
// ---- どちらか必須 ----
// DiscordのWebhook URL: DISCORD_WEBHOOK_URL
// SlackのWebhook URL: SLACK_WEBHOOK_URL
// ---- 任意(Discordのみ設定可能) ----
// Discordに投稿する際のユーザー名: USER_NAME
// Discordに投稿する際のアイコン画像URL: AVATAR_URL

// 元ネタ: https://ajike.github.io/slack-bot-google-calendar/
// for Discord
function notifyTaskDiscord() {
  var taskList = "";
  taskList = taskListup(PropertiesService.getScriptProperties().getProperty("CALENDAR_ID"), 1);

  if (taskList != "") {
    var message = "## 明日のご予定をお知らせします。\n\n### ▼明日の予定▼\n" + taskList;  // 通知内容のメッセージ
    postDiscord(message);//postしたくないならココをコメントアウト
    Logger.log(message);
  }
}

function notifyTaskDiscordToday() {
  var taskList = "";
  taskList = taskListup(PropertiesService.getScriptProperties().getProperty("CALENDAR_ID"), 0);

  if (taskList != "") {
    var message = "## 本日のご予定をお知らせします。\n\n### ▼本日の予定▼\n" + taskList;  // 通知内容のメッセージ
    postDiscord(message);//postしたくないならココをコメントアウト
    Logger.log(message);
  }
}

// for Slack
function notifyTaskSlack() {
  var taskList = "";
  taskList = taskListup(PropertiesService.getScriptProperties().getProperty("CALENDAR_ID"), 1);

  if (taskList != "") {
    var message = "## 明日のご予定をお知らせします。\n\n### ▼明日の予定▼\n" + taskList;  // 通知内容のメッセージ
    postSlack(message);//postしたくないならココをコメントアウト
    Logger.log(message);
  }
}

// Discord and Slack !!!!!!!!!!!!!!!!!!!!!!!
function notifyTaskDiscordAndSlack() {
  var taskList = "";
  taskList = taskListup(PropertiesService.getScriptProperties().getProperty("CALENDAR_ID"), 1);

  if (taskList != "") {
    var message = "## 明日のご予定をお知らせします。\n\n### ▼明日の予定▼\n" + taskList;  // 通知内容のメッセージ
    postSlack(message);//postしたくないならココをコメントアウト
    postDiscord(message);//postしたくないならココをコメントアウト
    Logger.log(message);
  }
}

function taskListup(cal_id, day) {
  var list = "";
  var cal = CalendarApp.getCalendarById(cal_id);
  var tomorrow = new Date();
  tomorrow.setDate( tomorrow.getDate() + day ); // 明日の日付を取得
  tomorrow.setHours(0, 0, 0, 0);

  var events = cal.getEventsForDay(tomorrow);
  var prefix = "- " // 各タスクの頭につく接頭辞
  
  for(var i=0; i < events.length; i++){
    var task = "";
    var additionalInformation = false;
    var info = "(";
    var start = events[i].getStartTime();
    var end = events[i].getEndTime();
    var place = events[i].getLocation();
    var description = events[i].getDescription();

    var nextDay = new Date();
    nextDay.setDate( tomorrow.getDate() + 1 ); // さらに次の日の日付を取得
    nextDay.setHours(0, 0, 0, 0);

    task = prefix + events[i].getTitle();
 
    if(start) {
      additionalInformation = true;      
      // 明日開始よりも以前に開始している場合、表示を変える
      if (start.getTime() <= tomorrow.getTime()) {
        if (end.getTime() == nextDay.getTime()) {// 終日
          info += Utilities.formatDate(start, "JST", "MM/dd (E)") + " 終日";
        } else {
          info += Utilities.formatDate(start, "JST", "MM/dd (E) HH:mm") + "-" + Utilities.formatDate(end, "JST", "MM/dd (E) HH:mm");
        }
      } else {
        info += Utilities.formatDate(start, "JST", "HH:mm") + "-" + Utilities.formatDate(end, "JST", "HH:mm");
      }
    }
    
    if (place) {
      additionalInformation = true;
      if (place.includes("http")) {
        info += " @ " + place + " )\n"
      } else {//httpじゃないなら、@にくっつける
        info += "@" + place + ")\n"
      }
    } else {// no place
      info += ")"
    }

    if (description) {
      additionalInformation = true;
      info += "> " + description.replace(/\n/g,"\n> ") + "\n"
    }

    if (additionalInformation) {
      list +=  task + info + "\n"
    } else {
      list += task + "\n";
    }
  }

  return list;
}

function postDiscord(message){
  const parse = 'full';
  const methods = 'post';
  const webhook = PropertiesService.getScriptProperties().getProperty("DISCORD_WEBHOOK_URL");
  let payload = null;

  var user_name = PropertiesService.getScriptProperties().getProperty("USER_NAME");
  if (!user_name) {
    user_name = 'カレンダー君';
  }
  var avatar_url = PropertiesService.getScriptProperties().getProperty("AVATAR_URL");
  if (!avatar_url) {
    avatar_url = 'https://cdn.discordapp.com/attachments/792765244040675389/921661726863282176/pngegg.png';
  }

  payload = {
    'content' : message,
    'username'   : user_name,
    'avatar_url' : avatar_url,
    'parse': parse,
  };

  const params = {
    'method': methods,
    'payload' : payload,
    'muteHttpExceptions': true,
  };

  response = UrlFetchApp.fetch(webhook, params);

  Logger.log(payload);
  Logger.log(response);
}

function postSlack(message) {
    var payload = {
      "text" : message.replace(/#{1,3}/g,"") // Discordでのみ有効な見出しを削除
    }

  var options = {
    "method" : "POST",
    "payload" : JSON.stringify(payload)
  }

  var response = UrlFetchApp.fetch(PropertiesService.getScriptProperties().getProperty("SLACK_WEBHOOK_URL"), options);
  Logger.log(payload);
  Logger.log(response.getContentText("UTF-8"));
}
  • 上の保存ボタン(orCtrl+S)をクリックしてください
  • 保存されると、保存ボタンがグレーアウトされ、デバッグの右のプルダウンメニューが選択できるようになります
    保存したあと、プルダウンメニューがいろいろ選べるyouninaru
プロジェクト設定(カレンダーIDの設定)
  • 左の歯車にカーソルをあてると、名前が表示されます
    • 歯車(プロジェクトの設定)をクリックします
      左のmenyu-
  • 一番下に、スクリプト プロパティがあります
    • ここにカレンダーIDやDiscordやSlackの投稿先URLなどのプロパティ(設定)を保存していきます
      スクリプト プロパティ
  • スクリプト プロパティを追加をクリックします
  • プロパティと値が入力できるようになるので、以下のように入力し、「スクリプト プロパティを保存」をクリックしてください
  • 保存後はうすくグレーアウトされ、編集できなくなります(編集したい場合、「スクリプト プロパティを編集」をクリックしてください)
    スクリプト プロパティの保存後画面

チャットツールによって違う部分

  • これで通知する部分(Google Apps Script)については作成が完了しました
  • 以降は、通知する先によって手順が変わりますので、記事が分かれています

  • 両方(DiscordとSlack)に通知することも可能です(上記記事を両方みたらわかると思います)

参考資料

  • 以下、技術ブログの内容を大いに参考にしております

engineer-blog.ajike.co.jp