概要
Googleカレンダーに入っている予定を、Trelloのカードとして自動で作ってくれるGASを作りました。
下記の記事を参考に作っています。
参考記事との相違点
「Backlogに並べて、実施するものはToDoへ、着手したら作業中へ」みたいなカンバン運用向けに調整しています。

また、運用を楽にするため、重複対策を入れています。
Backlog、ToDo、作業中のいずれかに同じ予定があれば、作成をスキップします。
作り方
用意するもの
- Google カレンダーID
- Trello APIキー
- Trello トークン
- Trello リストID
- Backlog
- ToDo
- 作業中
※Trello リストIDはPowerShellから確認可能です。
スプレッドシート
コード
今回のコードは、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);
}
}
}
