0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS】Googleカレンダーの予定をTrelloに転記してみた(重複しない版)

0
Last updated at Posted at 2026-01-04

概要

Googleカレンダーに入っている予定を、Trelloのカードとして自動で作ってくれるGASを作りました。
下記の記事を参考に作っています。

参考記事との相違点

「Backlogに並べて、実施するものはToDoへ、着手したら作業中へ」みたいなカンバン運用向けに調整しています。
スクリーンショット 2026-01-04 145505.png

また、運用を楽にするため、重複対策を入れています。
Backlog、ToDo、作業中のいずれかに同じ予定があれば、作成をスキップします。

作り方

用意するもの

  • Google カレンダーID
  • Trello APIキー
  • Trello トークン
  • Trello リストID
    • Backlog
    • ToDo
    • 作業中

※Trello リストIDはPowerShellから確認可能です。

スプレッドシート

スクリーンショット 2026-01-04 150803.png

コード

今回のコードは、ChatGPTに作ってもらっています。
※勉強用ではなく、完成が優先だったため。

function getCalendarEvents() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const CALENDAR_ID     = sheet.getRange(2, 2).getValue();
  const TRELLO_KEY      = sheet.getRange(3, 2).getValue();
  const TRELLO_TOKEN    = sheet.getRange(4, 2).getValue();
  const BACKLOG_ID      = sheet.getRange(5, 2).getValue();
  const TODO_LIST_ID    = sheet.getRange(6, 2).getValue();
  const WORKING_LIST_ID = sheet.getRange(7, 2).getValue();

  const calendar = CalendarApp.getCalendarById(CALENDAR_ID);

  // 対象期間
  const startTime = new Date();
  const endTime = new Date();
  endTime.setDate(endTime.getDate() + 7);
  const events = calendar.getEvents(startTime, endTime);

  const createCardUrl = `https://trello.com/1/cards?key=${TRELLO_KEY}&token=${TRELLO_TOKEN}`;

  // 重複チェック対象リスト(3つだけ)
  const CHECK_LIST_IDS = [
    BACKLOG_ID,
    TODO_LIST_ID,
    WORKING_LIST_ID
  ];

  const MARKER_PREFIX = "GCAL_EVENT_ID:";
  const existingEventIds = new Set();

  // 指定リストだけカードを取得
  for (const listId of CHECK_LIST_IDS) {
    const listCardsUrl =
      `https://trello.com/1/lists/${listId}/cards?fields=desc&limit=1000&key=${TRELLO_KEY}&token=${TRELLO_TOKEN}`;

    const res = UrlFetchApp.fetch(listCardsUrl);
    const cards = JSON.parse(res.getContentText());

    for (const card of cards) {
      const desc = card.desc || "";
      const m = desc.match(/GCAL_EVENT_ID:([^\s]+)/);
      if (m && m[1]) existingEventIds.add(m[1]);
    }
  }

  // 未登録イベントだけカード作成
  const tz = Session.getScriptTimeZone();

  for (const event of events) {
    const eventId = event.getId();
    if (existingEventIds.has(eventId)) continue;

    const title = event.getTitle();
    const evStart = event.getStartTime();
    const evEnd = event.getEndTime();

    const isAllDay = event.isAllDayEvent();
    const startStr = Utilities.formatDate(
      evStart,
      tz,
      isAllDay ? "yyyy/MM/dd" : "yyyy/MM/dd HH:mm"
    );
    const endStr = Utilities.formatDate(
      evEnd,
      tz,
      isAllDay ? "yyyy/MM/dd" : "yyyy/MM/dd HH:mm"
    );

    const dueIso = evEnd.toISOString();

    const desc = [
      `開始: ${startStr}`,
      `終了: ${endStr}`,
      `${MARKER_PREFIX}${eventId}`
    ].join("\n");

    const options = {
      method: "post",
      payload: {
        idList: BACKLOG_ID, // 作成先は Backlog
        name: title,
        desc: desc,
        due: dueIso
      },
      muteHttpExceptions: true
    };

    const res = UrlFetchApp.fetch(createCardUrl, options);

    // 同一実行中の重複も防止
    if (res.getResponseCode() >= 200 && res.getResponseCode() < 300) {
      existingEventIds.add(eventId);
    }
  }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?