4️⃣

S21. Google カレンダーからタスクを作成するアプリ

⚠️
補足情報
2023年3月現在、まだ Google カレンダーの同期データベースは実装されていません。そのため、この節は多くの読者が興味を持っており、実際に自分で実装したいと考えていると思います。そのため、ここについてはチェックボックス形式でワークシートの形にしてみました。

21.1 タスクデータベースの作成

  • 最初は練習のため、このテンプレートデータベースでテストしてみてください。うまく動いたら自分のタスクデータベースに差し替えるといいでしょう。
タスクデータベースを準備
タスクデータベースにインテグレーションキーをコネクト
タスクデータベースの URL を取得
タスクデータベースURL (例: https://www.notion.so/hkob/33a3ea2f301f46d781a5cdcb8dc90231?v=b1e5f0dc7f644d9abf63bc695788c4e1)
タスクデータベースの database_id を取得
タスクデータベースID (例: 33a3ea2f301f46d781a5cdcb8dc90231)

21.2 Google Calendar API の有効化

Google Calendar API を有効化

21.3 GAS 環境の準備

アプリに名前を設定
カレンダーサービスを追加

21.4 Google Calendar の準備

カレンダーID (hogehoge@group.calendar.google.com)
CALENDAR_ID プロパティを追加
NOTION_API_KEY プロパティを追加
DATABASE_ID プロパティを追加
NOTION_VERSION プロパティを追加

21.5 カレンダー認証

19節で紹介した以下の関数をコピーしておきます。同期ブロックで持ってきています。
scriptPropertyFor を追加
// スクリプトプロパティを取得 function scriptPropertyFor(key) { return PropertiesService.getScriptProperties().getProperty(key) }
スクリプトプロパティ取得関数
notionAPIKey を追加
notionVersion を追加
databaseID を追加
// NOTION_API_KEY を取得 function notionAPIKey() { return scriptPropertyFor("NOTION_API_KEY") } // NOTION_VERSION を取得 function notionVersion() { return scriptPropertyFor("NOTION_VERSION") } // DATABASE_ID を取得 function databaseID() { return scriptPropertyFor("DATABASE_ID") }
特定スクリプトプロパティ取得関数
saveScriptPropertyTo を追加
// スクリプトプロパティにデータを保存 function saveScriptPropertyTo(key, value) { PropertiesService.getScriptProperties().setProperty(key, value) }
スクリプトプロパティ設定関数
sendNotion を追加
// Notion に payload を send する function sendNotion(url_sub, payload, method) { const options = { "method": method, "headers": { "Content-type": "application/json", "Authorization": "Bearer " + notionAPIKey(), "Notion-Version": notionVersion(), }, "payload": payload ? JSON.stringify(payload) : null }; // デバッグ時にはコメントを外す // console.log(options) Utilities.sleep(400) const url = "https://api.notion.com/v1/" + url_sub return JSON.parse(UrlFetchApp.fetch(url, options)) }
Notionに payload を送信する
createPage を追加
// Create Page API を呼び出す function createPage(payload) { return sendNotion("pages", payload, "POST") }
Create page API を呼び出す
updatePage を追加
// Update Page API を呼び出す function updatePage(page_id, payload) { return sendNotion("pages/" + page_id, payload, "PATCH") }
Update page API を呼び出す
readCalendar までを記述
// CALENDAR_ID を取得 function calendarID() { return scriptPropertyFor("CALENDAR_ID") } // 前回保存したカレンダーのSyncTokenを取り出す、前回保存分が無い場合は今回のSyncTokenを利用する function getSyncToken(calendar_id) { const token = PropertiesService.getScriptProperties().getProperty("SYNC_TOKEN") if (token) { return token } const events = Calendar.Events.list(calendar_id, {"timeMin": (new Date()).toISOString()}) return events.nextSyncToken } // SyncTokenをプロパティに保存する function saveSyncToken(token) { saveScriptPropertyTo("SYNC_TOKEN", token) } // 初回認証のためだけのテスト呼び出し関数 -> 現在の SYNC_TOKEN を保存 function readCalendar() { const token = getSyncToken(calendarID()) saveSyncToken(token) console.log(token) }
readCalendar を実行して認証

21.6 カレンダーイベントによるトリガ

eventToDateHash を追加
// カレンダーイベントから日付の hash を作成 (削除時にも呼ばれるので、イベントに日付が設定されていなければ null を返却) function eventToDateHash(e) { let hash = null if ("dateTime" in e.start) { hash = { "start": e.start.dateTime, "end": e.end.dateTime } } if ("date" in e.start) { const sday_str = e.start.date hash = { "start": sday_str } let eday = new Date(e.end.date) eday.setDate(eday.getDate() - 1) const eday_str = eday.toISOString().split('T')[0] if (sday_str != eday_str) { hash["end"] = eday_str } } return hash }
カレンダーイベントの hash を作成
updatePayload を追加
// title と date_hash から update payload を作成 function updatePayload(title, date_hash) { return { "properties": { "日付": {"date": date_hash}, "タスク名": {"title": [{"text": {"content": title}}]} } } }
updatePayload
createPayload を追加
// title, date_hash から create payload を作成 function createPayload(title, date_hash) { return { "parent": {"database_id": databaseID()}, "properties": { "日付": {"date": date_hash}, "タスク名": {"title": [{"text": {"content": title}}]}, } } }
createPayload
doCalendarPost を追加
// カレンダーイベントが変更されたら呼ばれるメソッド function doCalendarPost(event) { // カレンダーIDのの取得 const calendar_id = calendarID() // 前回実行時に取得したカレンダーTokenの取得 let token = getSyncToken(calendar_id) // Token から後のカレンダーを取得 const events = Calendar.Events.list(calendar_id, {"syncToken": token}) // ステータスを見て登録、もしくは更新の予定のみにフィルタリング const filtered_items = events.items.filter(e => { return e.status == "confirmed" }) // 今回のTokenを保存する(次回のScript実行時に利用) saveSyncToken(events.nextSyncToken) filtered_items.forEach(e => { const descriptions = (e.description || "").split("\n") const dlen = descriptions.length let exist = false let id = "-" // 行が存在する場合 if (dlen > 0) { // 最後の行を取得 const last_line = descriptions[dlen - 1] // id を取得 const ids = last_line.match(/^id:(.*)$/) // 取得できた場合 if (ids != null && ids[1].length > 31) { // 取得した id id = ids[1]; exist = true } } // イベント日付を取得 const date_hash = eventToDateHash(e) // イベントに日付がある場合だけ実施 if (date_hash) { // 存在した場合は update if (exist) { const payload = updatePayload(e.summary, date_hash) updatePage(id, payload) } else { const payload = createPayload(e.summary, date_hash) const ans = createPage(payload) id = ans["id"] // 作成したページの id をイベントに追加 descriptions.push("id:" + id) e.description = descriptions.join("\n") Calendar.Events.patch(e, calendar_id, e.id) } } }) }
doCalendarPost

21.7 テスト実行

test 関数を追加
function test() { const calendar_id = calendarID() const token = getSyncToken(calendar_id) const events = Calendar.Events.list(calendar_id, {"syncToken": token}) const event = events.items[0] doCalendarPost(event) }
カレンダーの変更をトリガに設定
カレンダーにイベントを追加して、Notion データベースに追加されることを確認
 
⚠️
補足説明
今回、Google カレンダーから Notion に登録するアプリを GAS で作成しました。逆に Notion から Google カレンダーに登録するのを自動化するのは少し面倒です。ただし、特定のタスクを Google カレンダーに登録するのであれば、簡単に実装できます。これを実現するための手段として、最近 NotionToCalendar というショートカットアプリを作成しました。興味があったらブログ記事をご覧ください。