はじめに
土曜日に書いた「定期的に Notion ページを作成する : Notion 解説(19) - hkob’s blog」によって、毎朝「雑務・振り返り」タスクが Notion に自動登録されるようになりました。
実は、繰り返しタスクについては、まいにち Notion No.7 を参考にして、「リマインダの作成: Notion 解説(7) - hkob’s blog」において、リマインダテーブルという形で LaunchPad に表示されるようになっています。ただ、リマインダはだたのリマインダであって、実際にタスクにしたければ、別途タスクテーブルにタスクを作成する必要がありました。
せっかくなので、この部分も自動タスク作成にしてみようと思いました。Yuka さんの基準日からのインターバルというアイディアだけでなく、指定月、指定週、指定日なども設定できるように拡張しています。
準備
スプレッドシートの作成
作成したスプレッドシートはこんな感じです。各行が表示される条件になります。複数の条件がある場合には AND 条件になっています。各列の説明をします。
- 雑務・振り返り: インターバルが1なので毎日表示されます。
- 燃えるゴミの日、資源ゴミの日、週報: 週ごとなのでインターバルが 7 になっています。基準日を変えることで特定の週に設定できます。OR は書けないので複数行に同じものを書いてください。
- 燃えないゴミの日: 月の第2・第4火曜日なので、インターバルだけでなく、特定の週を設定しています。これも OR は書けないので 2 行書いています。
- 月報: 月末に表示したいので最終日を指定します。ただし最終日は月毎に異なるので、-1 で指定します(-2 だと月末の前日)。正の値の場合には日付が一致すると表示します。
- 誕生日: 月と日の両方を設定します。1年に1回だけ表示されることになります。
スクリプトの修正
GAS のスクリプトは以下のように変更しました。中身はこれまでとほとんど変わりません。 (6/13修正: getMonth() の戻り値は0〜11でした。一月後に誕生日と言われて気づきました。以下のスクリプトは修正しています)
function readCalendar() { let calendarId = "この部分にカレンダーIDを書きます。"; let token = getSyncToken(calendarId); Logger.log(token); } // Notion に payload をポストして登録 function postNotion(payload){ let MY_NOTION_TOKEN = "この部分にIntegration Token を書きます。"; let url = "https://api.notion.com/v1/pages"; // API URL let options = { "method" : "POST", "headers": { "Content-type": "application/json", "Authorization": "Bearer " + MY_NOTION_TOKEN, "Notion-Version": "2021-05-13", }, "payload" : JSON.stringify(payload) }; Logger.log(options); UrlFetchApp.fetch(url, options); } // title と date_hash から payload を作成 function createPayload(title, date_hash) { let DATABASE_ID = "この部分にDatabase ID を書きます"; return { "parent": { "database_id": DATABASE_ID }, "properties": { "タスク名": { "title": [ { "text": { "content": title } } ] }, "日付": { "type": "date", "date": date_hash }, "非ポモ?": { "type": "checkbox", "checkbox": true } } } } // カレンダーイベントが変更されたら呼ばれるメソッド function doCalendarPost(event) { let calendarId = event.calendarId; // カレンダーIDのの取得 let token = getSyncToken(calendarId); // 前回実行時に取得したカレンダーTokenの取得 let events = Calendar.Events.list(calendarId, {'syncToken': token}); // Token から後のカレンダーを取得 let filteredItems = events.items.filter(e => {return e.status == "confirmed"}); // ステータスを見て登録、もしくは更新の予定のみにフィルタリング filteredItems.map(e => {return eventToPayload(e)}).filter(e => {return e}).forEach(p => { postNotion(p); }); saveSyncToken(events.nextSyncToken); // 今回のTokenを保存する(次回のScript実行時に利用) } // 前回保存したカレンダーのSyncTokenを取り出す、前回保存分が無い場合は今回のSyncTokenを利用する function getSyncToken(calendarId){ var token = PropertiesService.getScriptProperties().getProperty('SYNC_TOKEN'); if (token) { return token; } let events = Calendar.Events.list(calendarId, {'timeMin': (new Date()).toISOString()}); return events.nextSyncToken; } // カレンダーイベントから payload を作成 (削除時にも呼ばれるので、イベントに日付が設定されていなければ null を返却) function eventToPayload(e) { let hash = null; if ('dateTime' in e.start){ hash = { "start": e.start.dateTime.replace("T", " "), "end": e.end.dateTime.replace("T", " ") } } if ('date' in e.start) { hash = { "start": e.start.date } } return hash ? createPayload(e.summary, hash) : null; } // SyncTokenをプロパティに保存する function saveSyncToken(token){ PropertiesService.getScriptProperties().setProperty('SYNC_TOKEN', token); } function weekToStr(date) { let w = Utilities.formatDate(date, "JST", "u"); return ["日", "月", "火", "水", "木", "金", "土", "日"][w] } // date の日付がテーブルの行ごとに当てはまるか確認し、当てはまれば Notion にポスト // デバッグテスト用に date を変えながらテストしていた function selectEvents(date) { var sheet = SpreadsheetApp.getActiveSheet(); var lastrow = sheet.getLastRow(); for (let row = 1; row < lastrow; row++) { let use = false; let lines = sheet.getRange("A"+(row+1)+":G"+(row+1)).getValues()[0]; if (isValidEvent(lines, date)) { let title = lines[0] + ((lines[1] && lines[1].toFixed() == 1) ? Utilities.formatDate(date, "JST", " M/d (" + weekToStr(date) + ")") : ""); let date_hash = {"start": Utilities.formatDate(date, "JST", "yyyy-MM-dd")}; postNotion(createPayload(title, date_hash)); } } } // lines の繰り返しイベント情報を元に date が当てはまる時に true を返す // and 条件なので、一つでも条件に満足しない時に false を返す (最後まで引っ掛からなければ true) function isValidEvent(lines, date) { let aDay = 86400000; // 基準日とインターバルが設定されている時、インターバルの倍数の日以外なら false let baseDate = lines[2]; let interval = lines[3] && lines[3].toFixed(); if (baseDate && interval > 0) { let diff = ((date - baseDate) / aDay).toFixed(); if (diff % interval != 0) { return false; } } // 月が設定されている時、月が一致しなければ false let month = lines[4] && lines[4].toFixed(); if (month > 0 && month != date.getMonth()+1) { return false; } let day = lines[6] && lines[6].toFixed(); // 日(day)が設定されていて day > 0 なら (day - 1)日前が 1 日でなければ false if (day > 0 && new Date(date.getYear(), date.getMonth(), date.getDate() - (day - 1)).getDate().toFixed() != 1) { return false; } // 日(day)が設定されていて day < 0 なら |day|日後が 1 日でなければ false if (day < 0 && new Date(date.getYear(), date.getMonth(), date.getDate() - day).getDate().toFixed() != 1) { return false; } // 週(week)が設定されていて、(date.day - 1) / 7 == week - 1 でなければ false let week = lines[5] && lines[5].toFixed(); if (week > 0 && ((date.getDate() - 1) / 7).toFixed() != (week - 1).toFixed()) { return false; } return true; } // これまで通り、毎朝呼ばれる関数 function doEverydayPost() { selectEvents(new Date()); }
実行
とりあえずテストでコマンドを実行したが、月曜日なので今朝と同じものしか登録されなかった。水曜日に燃えるゴミの日が表示されれば成功かな。
5/18 追記
115 行目に以下の行を追加し、ログを残すようにしてみました。
sheet.getRange("I" + (row+1)).setValue(date);
こんな感じで最後に登録した日が Spreadsheet に残るようになります。