Googleカレンダーを同期するGASを書いた

最近は、イベントに参加することも多く、複数のGoogleアカウント感でスケジュールを手動コピーすることが多くなってきました. さすがに手動コピーは面倒なので、複数のGoogleカレンダーから1つのカレンダーへ同期するようなスクリプトを書きました.
前提
- Google Apps ScriptやFusion Tablesについて説明はしない
- Google Apps ScirptsからFusion Tables APIが使えるようになっていること
Googleカレンダーを同期する
今回作るスクリプトは、以下を満たすことを目的とします.
- 複数のカレンダーから、特定のカレンダーへ予定が登録される
- 登録先のカレンダーへは直接予定が登録されることもあるため、予定を削除することはしない
- 定期実行した際に予定が重複登録されないこと
- 登録元の予定が変更・削除された場合は考慮しなくてよい
1つ目と2つ目は特に難しい話ではないですが、3つ目の「定期実行した際に予定が重複登録されないこと」は、どこかに同期した情報を保存しておく必要があります.
そこで今回はSQLでデータを操作可能なFusion Tables
を使い、同期した情報を保存することにしました.
var config = {
// 統合先カレンダーID
destCalId: "XXX",
// 統合元カレンダーID
srcCalIds: [
"YYY"
],
// 同期対象日数
syncDays: 365,
// Fusion TableのID
tableId: 'ZZZ',
};
function main() {
var startDate = new Date();
var endDate = new Date((new Date()).setDate(startDate.getDate() + config.syncDays));
var destCal = getCalendarById(config.destCalId);
for (var i = 0; i < config.srcCalIds.length; i++) {
var srcCal = getCalendarById(config.srcCalIds[i]);
copyEvents(destCal, srcCal, startDate, endDate);
}
}
function getCalendarById(id) {
return CalendarApp.getCalendarById(id);
}
function getEvents(cal, startDate, endDate) {
return cal.getEvents(startDate, endDate);
}
function copyEvents(destCal, srcCal, startDate, endDate) {
var events = getEvents(srcCal, startDate, endDate);
for (var i = 0; i < events.length; i++) {
var event = events[i];
var table = new SyncTable(config.tableId);
if (table.notExists(event)) {
var destEvent = createEvent(destCal, event);
table.save(event, destEvent);
} else {
Logger.log("Already synced event: " + event.getId());
}
}
}
function createEvent(destCal, event) {
if (event.isAllDayEvent()) {
return destCal.createAllDayEvent(
event.getTitle(),
event.getStartTime(),
event.getEndTime(),
{
description: event.getDescription(),
location: event.getLocation()
}
);
} else {
return destCal.createEvent(
event.getTitle(),
event.getStartTime(),
event.getEndTime(),
{
description: event.getDescription(),
location: event.getLocation()
}
);
}
}
// 同期確認用テーブルへのI/F
function SyncTable(id) {
this.id = id;
// 同期前か確認
this.notExists = function(event) {
var query = "SELECT * FROM " + this.id + " WHERE 'SrcEvtId' = '" + event.getId() + "'";
var result = FusionTables.Query.sql(query);
return result.rows === undefined || result.rows.length === 0;
};
// 同期履歴を保存
this.save = function(srcEvent, destEvent) {
var query = "INSERT INTO " + this.id + " (SrcEvtId, DestEvtId) VALUES ('" + srcEvent.getId() + "', '" + destEvent.getId() + "');"
var result = FusionTables.Query.sql(query);
};
}
処理の大筋の流れとしては、
- 登録元のカレンダーから予定を取得する
- 予定がすでに登録済みか確認する
- 登録されている場合は次の予定へ、登録されていない場合はイベントを登録する
- イベントを登録する
になります. ちょっと気まぐれで書きすぎて、Fusion Tablesへの操作の処理が適当になってしまいました. 自分しかいじらないし、動けばいいんですよ. 動けば.
config
スクリプトの設定をまとめたオブジェクトです. 適切な値で登録しておくことが重要です
var config = {
// 統合先カレンダーID
destCalId: "XXX",
// 統合元カレンダーID
srcCalIds: [
"YYY"
],
// 同期対象日数
syncDays: 365,
// Fusion TableのID
tableId: 'ZZZ',
};
main関数
定期実行トリガーが呼び出すための関数です. ここでは「同期期間の設定」「登録先カレンダーの取得」「登録元カレンダー分、予定のコピー関数の呼び出し」を役割にしています.
function main() {
var startDate = new Date();
var endDate = new Date((new Date()).setDate(startDate.getDate() + config.syncDays));
var destCal = getCalendarById(config.destCalId);
for (var i = 0; i < config.srcCalIds.length; i++) {
var srcCal = getCalendarById(config.srcCalIds[i]);
copyEvents(destCal, srcCal, startDate, endDate);
}
}
getCalendarById関数
指定されたIDのカレンダーをオブジェクトを取得するだけの関数です. 正直関数化する必要もなかったかな.
function getCalendarById(id) {
return CalendarApp.getCalendarById(id);
}
getEvents関数
引数に指定されたカレンダーから、指定期間の予定を取得する関数です. こちらも関数化する必要は特になかったかな…
function getEvents(cal, startDate, endDate) {
return cal.getEvents(startDate, endDate);
}
copyEvents関数
登録元のカレンダーの予定を、登録先のカレンダーへ登録する関数です. Fusion Tablesに存在確認をし、存在しなければ登録するための関数を呼び出します. 登録完了後、Fusion Tablesに同期した情報を保存します. 少し役割をもたせすぎていて、見通しが悪くなっています. とはいえ、登録用の関数でFusion Tablesへの操作を行わせるのは良くないので、ここに書いてしまいました.
function copyEvents(destCal, srcCal, startDate, endDate) {
var events = getEvents(srcCal, startDate, endDate);
for (var i = 0; i < events.length; i++) {
var event = events[i];
var table = new SyncTable(config.tableId);
if (table.notExists(event)) {
var destEvent = createEvent(destCal, event);
table.save(event, destEvent);
} else {
Logger.log("Already synced event: " + event.getId());
}
}
}
createEvent関数
カレンダーへ予定を登録する関数です. 1日予定と時間指定の予定によっては、登録する関数を呼び分けています. 引数が一緒なので、うまいことまとめたいですね.
function createEvent(destCal, event) {
if (event.isAllDayEvent()) {
return destCal.createAllDayEvent(
event.getTitle(),
event.getStartTime(),
event.getEndTime(),
{
description: event.getDescription(),
location: event.getLocation()
}
);
} else {
return destCal.createEvent(
event.getTitle(),
event.getStartTime(),
event.getEndTime(),
{
description: event.getDescription(),
location: event.getLocation()
}
);
}
}
SyncTableオブジェクト
何故かオブジェクト化してしまったFusion Tables関連の操作をまとめたオブジェクトです. 関数の定義の仕方はバッドプラクティスの塊なので、絶対に真似しないようにしましょう. というか、なんでこんなオブジェクト書いたんだ…
// 同期確認用テーブルへのI/F
function SyncTable(id) {
this.id = id;
// 同期前か確認
this.notExists = function(event) {
var query = "SELECT * FROM " + this.id + " WHERE 'SrcEvtId' = '" + event.getId() + "'";
var result = FusionTables.Query.sql(query);
return result.rows === undefined || result.rows.length === 0;
};
// 同期履歴を保存
this.save = function(srcEvent, destEvent) {
var query = "INSERT INTO " + this.id + " (SrcEvtId, DestEvtId) VALUES ('" + srcEvent.getId() + "', '" + destEvent.getId() + "');"
var result = FusionTables.Query.sql(query);
};
}
最後に
今回は、Googleカレンダーを同期するためにGoogle Apps Scriptでスクリプトを書きました. 変な実装になってしまったので反省です. そのうち予定の変更・削除に対応させようかなと思います.