Google Calendarから予定を取得してLINEで自動でリマインドする

公開日: 

やりたいこと

毎週月曜日の朝に、今週の予定をGoogle Calendarから取得して、下の画像のようにLINEにメッセージを送信したい。

利用するサービス

  • Google API: Googleカレンダーから予定を取得する
  • LINE API: LINEにメッセージを送信する
  • Render.com: 毎週月曜日の朝に定期処理を実行する

それぞれ個別のサービスの使い方については記事を書いているのでよかったら、読んでみてください。今回はそれらのサービスを組み合わせた必殺技です。

https://www.yukendev.com/blogs/tips-googleapi-calendar

https://www.yukendev.com/blogs/tips-line-api

https://www.yukendev.com/blogs/tips-render-com-cron-job

完成したコード

https://github.com/yukendev/shin.jing.rui-bot

ポイント

以下は、毎週月曜日の午前9時半に実行されるコード

import { getEventListFromGoogleCalendar } from "../api/google.js";
import { sendLineMessage } from "../api/line.js";
import dotenv from "dotenv";
import { parseCalendarEvent, parseToLineMessage } from "../utils/calendar.js";
import dayjs from "dayjs";

/*
   日本時間で毎週月曜日の午前9時半に実行されるジョブ
 */
dotenv.config();

const myId = process.env.YUKEN_LINE_ID; // 自分のLINEのID
const shinjingruiGroupId = process.env.SHINJINGRUI_LINE_GROUP_ID; // グループのLINEのID

const sendGoogleCalendarEvents = async () => {
  const minDate = dayjs().set("hour", 0).set("minute", 0); // 今日の00時00分
  const maxDate = minDate.add(7, "d").set("hour", 23).set("minute", 59); // 1週間後の23時59分

  try {
		// 今日の00時00分から1週間後の23時59分までの予定を取得
    const result = await getEventListFromGoogleCalendar(
      minDate.format(),
      maxDate.format()
    );

    if (!result) return;

    const parsedData = parseCalendarEvent(result);
    const lineMessage = parseToLineMessage(parsedData);

    if (shinjingruiGroupId !== undefined) {
      sendLineMessage(
        shinjingruiGroupId,
        "今週の予定です!今週もゆっくり頑張りましょう🐈‍⬛"
      );
      sendLineMessage(shinjingruiGroupId, lineMessage);
    }
  } catch (err) {
    if (myId !== undefined) {
      sendLineMessage(myId, "カレンダーイベント取得中にエラーが発生しました🚨");
    }
  }
};

sendGoogleCalendarEvents();

以下はカレンダーの予定取得部分です。詳しくは別の記事で解説していますが、googleのapiを利用して、指定された範囲の予定一覧を取得しています。

import dotenv from "dotenv";
import { google } from "googleapis";

dotenv.config();

const clientId = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
const refreshToken = process.env.GOOGLE_OAUTH_REFRESH_TOKEN;
const calendarId = process.env.CALENDAR_ID;

const getGoogleOAuth = async () => {
  const googleOAuth = new google.auth.OAuth2(
    clientId,
    clientSecret,
    "http://localhost"
  );

  // 毎回のリクエスト時に新しいアクセストークンを取得
  googleOAuth.setCredentials({
    refresh_token: refreshToken,
  });

  try {
    const accessTokenResponse = await googleOAuth.getAccessToken();

    const accessToken = accessTokenResponse.token;

    if (!accessToken)
      throw new Error("有効なアクセストークンを取得できませんでした");

    googleOAuth.setCredentials({
      access_token: accessToken,
    });

    return googleOAuth;
  } catch (err) {
    console.log("エラーの中身", err);
    throw new Error("アクセストークン取得時にエラーが発生しました");
  }
};

/**
 * @param timeMin new Date().toISOString() etc...
 * @param timeMax new Date().toISOString() etc...
 */
export const getEventListFromGoogleCalendar = async (
  timeMin: string,
  timeMax: string
) => {
  try {
    const googleOAuth = await getGoogleOAuth();
    const calendar = google.calendar({ version: "v3", auth: googleOAuth });
    const res = await calendar.events.list({
      calendarId,
      timeMin,
      timeMax,
      timeZone: "Asia/Tokyo",
    });
    if (!res.data.items)
      throw new Error("正常にイベントを取得できませんでした");
    const events = res.data.items.reverse();
    return events;
  } catch (err) {
    console.log("カレンダーからイベント取得時にエラーが発生しました", err);
  }
};

以下はRender.comでデプロイする用のrender.ymlファイルです。

render.yml
services:
  # 日本時間の毎週土曜日の午前9時30分に実行
  - type: cron
    name: saturday job
    runtime: node
    schedule: "30 0 * * 6"
    buildCommand: pnpm install && pnpm run build
    startCommand: pnpm run job:saturday
    region: singapore

  # 日本時間の毎週月曜日の午前9時30分に実行
  - type: cron
    name: monday job
    runtime: node
    schedule: "30 0 * * 1"
    buildCommand: pnpm install && pnpm run build
    startCommand: pnpm run job:monday
    region: singapore

  # 日本時間の毎月1日の午後12時に実行
  - type: cron
    name: monthly job
    runtime: node
    schedule: "0 3 1 * *"
    buildCommand: pnpm install && pnpm run build
    startCommand: pnpm run job:monthly
    region: singapore

今回の予定を取得してLINEで通知する以外の定期処理の設定も入っていますが、これでRender.comに簡単にデプロイすることができます。

完成!

これでこんな感じのLINEが毎週月曜に自動的に送信されます!ハッピー!

最後に

自分で作ったシステムが実際に身近なところで動いて、役に立っていると嬉しいですね。

では

Bye