1

If I search on the web something like " Notion API with Google Calendar", I only find solution related to Automate.io or Zappier.

Is there a way to use these powerful APIs to integrate Notion with Google Calendar, without rely on those sites? Which programming language can I use to do that? Do I need a server to run the program periodically?

PS: On YT I found something with Python, but I cannot find a complete guide to use these tools

nn65
  • 31
  • 1
  • 1
  • 7

1 Answers1

4

Which programming language can I use to do that?

You can easily use Google App Script and Notion API to achieve this. The syntax of Google app script is based on javascript (although some ES6 features are not supported). With the CalendarApp, you can practically control all the content on your Google Calendar.

Do I need a server to run the program periodically?

No you don't need to. In Google App Script, you can add time-driven triggers to let it start automatically.

Here's a simple example to sync notion to calendar:

Use UrlFetchApp to post query to your Notion database

In this case, I will set the date of the event, according to 'DeadLine' property. So I attached a filter to exclude empty values.

enter image description here

function main() {
    let data = {
        "filter": {
            "property": "DeadLine",
            "date": {
                "is_not_empty": true
            }
        }
    };

    let options = {
        'method': 'post',
        'contentType': 'application/json',
        'headers': {
            Authorization: 'Bearer ' + 'your integration token',
            'Notion-Version': '2021-05-13',
            'Content-Type': 'application/json',
        },
        'payload': JSON.stringify(data)
    };

    let response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/your_database_id/query', options);
    response = JSON.parse(response.getContentText());

    //other code
}

Create a new calendar event

The page is one of the response.results array. Here we use page.id and database_id as the unique identifier of the event. And need to distinguish whether the deadline contains time or only date.

There are two variables to deal with here:

  • have end date or not
  • have time or only date
const calendar = CalendarApp.getCalendarById("your calendar id");
const onlyDateRegex = /(\d{4})-(\d{2})-(\d{2})\b/;
const dateTimeRegex = /(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\.(\d{3})\+(\d{2})\:(\d{2})\b/;

function create(page) {
    let deadLine = page.properties.DeadLine;
    let title = "";
    page.properties.Name.title.forEach((rich_text) => {
        title += rich_text.plain_text;
    })
    let pageId = page.id + " in database " + page.parent.database_id;

    let startDate = deadLine.date.start;
    let endDate = deadLine.date.end;
    let startDateObj = new Date(startDate);
    let endDateObj = new Date(endDate);

    let evnet;
    Logger.log("Create page " + title);

    if (deadLine.date.end !== null) {
        if (onlyDateRegex.test(startDate))
            evnet = calendar.createAllDayEvent(title, startDateObj, endDateObj);

        if (dateTimeRegex.test(startDate))
            evnet = calendar.createEvent(title, startDateObj, endDateObj);

    } else {
        if (onlyDateRegex.test(startDate))
            evnet = calendar.createAllDayEvent(title, startDateObj);

        if (dateTimeRegex.test(startDate))
            evnet = calendar.createEvent(title, startDateObj, startDateObj);
    }
    evnet.setDescription(pageId);
}

Then we can use the id in the description to search for pages that have been synchronized

Here continues the main function in the first step.

function main() {

    //other code

    response = JSON.parse(response.getContentText());

    for (let i = 0; i < response.results.length; i++) {
        let pageId = response.results[i].id + " in database " + response.results[i].parent.database_id;
        let event = search(pageId);
        if (event === null) create(response.results[i]);
        else update(event, response.results[i]);
    }
}

function search(str) {
    let events = calendar.getEvents(new Date("1970-1-1"), new Date("2100-1-1"), {
        search: str
    });
    if (events.length > 1) throw new Error("uuid duplicate in search");
    if (events.length === 0) return null;
    return events[0];
}

If no result is found, create a new event, otherwise, try to update it.

The practice of updating an event is almost the same as when it was created

function update(event, page) {

    let deadLine = page.properties.DeadLine;
    let title = "";
    page.properties.Name.title.forEach((rich_text) => {
        title += rich_text.plain_text;
    })

    Logger.log("Update page " + title);

    let startDate = deadLine.date.start;
    let endDate = deadLine.date.end;
    let startDateObj = new Date(startDate);
    let endDateObj = new Date(endDate);

    if (deadLine.date.end !== null) {
        if (onlyDateRegex.test(startDate)) {
            startDateObj.setHours(0, 0, 0, 0);
            endDateObj.setHours(0, 0, 0, 0);
            if (event.isAllDayEvent()) {
                if ((event.getAllDayStartDate().getTime() !== startDateObj.getTime()) ||
                    (event.getAllDayEndDate().getTime() !== endDateObj.getTime())) {
                    Logger.log("update allDayStartDate " + event.getAllDayStartDate() + " to " + startDateObj);
                    Logger.log("update allDayEndDate " + event.getAllDayEndDate() + " to " + endDateObj);
                    event.setAllDayDates(startDateObj, endDateObj);
                }
            } else event.setAllDayDates(startDateObj, endDateObj);
        }
        if (dateTimeRegex.test(startDate)) {
            if (event.isAllDayEvent()) {
                Logger.log("change to dateTime, start: " + startDateObj + " end: " + endDateObj);
                event.setTime(startDateObj, endDateObj);
            } else {
                if ((event.getStartTime().getTime() !== startDateObj.getTime()) ||
                    (event.getEndTime().getTime() !== endDateObj.getTime())) {
                    Logger.log("update dateTime, start: " + startDateObj + " end: " + endDateObj);
                    event.setTime(startDateObj, endDateObj);
                }
            }
        }
    } else {
        if (onlyDateRegex.test(startDate)) {
            startDateObj.setHours(0, 0, 0, 0);
            if (event.isAllDayEvent()) {
                if ((event.getAllDayStartDate().getTime() !== startDateObj.getTime()) ||
                    (event.getAllDayEndDate().getTime() !== startDateObj.getTime())) {
                    Logger.log("update allOneDayDate " + event.getAllDayStartDate() + " to " + startDateObj);
                    event.setAllDayDate(startDateObj);
                }
            } else {
                Logger.log("change to allOneDayDate: " + startDateObj);
                event.setAllDayDates(startDateObj);
            }
        }
        if (dateTimeRegex.test(startDate)) {
            if (event.isAllDayEvent()) {
                Logger.log("change to dateTime: " + startDateObj);
                event.setTime(startDateObj, startDateObj);
            } else {
                if (event.getStartTime().getTime() !== startDateObj.getTime()) {
                    Logger.log("update dateTime: " + startDateObj);
                    event.setTime(startDateObj, startDateObj);
                }
            }
        }
    }
    if (event.getTitle() !== title) {
        Logger.log("update title: \"" + event.getTitle() + "\" to " + title);
        event.setTitle(title);
    }
}

Full example code

I am not a professional javascript or Google App Script engineer, these codes may still need improvement, hope this is useful.

Yukina
  • 109
  • 4