Automatically Register Completed TickTick Tasks to Notion

Table of Contents
Build a system to automatically register tasks completed in TickTick to a Notion database.
Overview
Notion allows for all-in-one information management, including note creation, Kanban-style management, and database building. While it offers benefits like building GUI flows with databases or stocking information, sometimes using tools specialized for specific purposes provides higher convenience. In this case, we will manage ToDo with TickTick and create a function to accumulate completed tasks in Notion for aggregation and review.
What to Do
We will accumulate tasks completed in TickTick into a Notion database. The general flow is as follows.
- Complete a task in TickTick
- Detect task completion with IFTTT and send a Webhook (POST) to GAS
- In GAS, handle Notion authentication and value conversion, then send a Webhook (POST)
- Insert the completed task into the Notion database
Tools like IFTTT are convenient for triggering events. However, since IFTTT makes it difficult to add authentication information to Webhook headers or convert values, we will use GAS to fill this gap.
Preparing to Use Notion API
Refer to Integrations > Steps to prepare the Notion side for API usage.
Database Construction
The information that can be carried via Webhook from TickTick through IFTTT is as above. Since there are no current disadvantages to retaining all data, we will create a database (table) in Notion to store this information.
Set date-related columns to Date type, Tag to MultiSelect type, and all others to String type. Once database construction is complete, go to the page's top-right Share menu, invite the created Integration, and enable API access.
GAS Construction
HTTP-POST Testing
[FYI] Memo on Parameters for Receiving IFTTT Webhook via GAS POST
Referring to the above, write sample code to receive the Webhook from IFTTT. Open the Google Apps Script Editor and input the following code.
function doPost(e) {
var jsonString = e.postData.getDataAsString()
const options = {name: "webhookTest"}
GmailApp.sendEmail("yourAlias@gmail.com", "webhookTest", jsonString, options)
}
Replace yourAlias
in yourAlias@gmail.com
with your Google account ID or any desired address. To test receiving POST, deploy the API.
- Click [Deploy]
- Click [New deployment]
- Click [Select type]
- Click [Web app]
- Fill in the form:
- Write a description (e.g., Receive a webhook from IFTTT)
- Select yourself for "Execute as"
- Set "Who has access" to "Anyone"
- Click [Deploy]
- Copy the web app URL and paste it into a notepad or similar
Once the steps are complete, use tools like ARC or IFTTT to send an HTTP-POST to the copied URL. The request content of the POST will be sent to the email address you set. If it doesn't work, check the deployment settings or code for errors.
Library Introduction
Introduce the library to operate the Notion API. The source code is as follows.
Error handling has been omitted, but it was written based on notion-sdk-py and the official documentation.
There might be individuals who have adapted the official library for GAS, so before using notion-sdk-gs
, check for reusable resources. Also, since the API might be updated to include more features, please refer to the official documentation as much as possible.
Below are the steps to introduce notion-sdk-gs
.
- From the GAS Editor's left side menu, click [Library]
- Enter the following in the script ID:
- 1C6kLuU1Ugclg-8C7hsbK221IgepJoGTrbh2VI4itdExvyFDCzc4adK8h
- Click [Look up]
- Enter "Notion" in the ID field
- Click [Add]
Once added, "Notion" will appear in the side menu libraries. If something other than "Notion" is displayed, click it and correct the ID to "Notion".
Registering Notion Token
Execute the following code to register the Notion Token in the GAS project. Replace {{yourToken}}
with the Token obtained in "Preparing to Use Notion API" and run setNotionToken
. After execution, run readProp
to confirm that the Token is displayed.
function setNotionToken() {
PropertiesService.getScriptProperties().setProperty("NOTION_TOKEN", "{{yourToken}}")
}
function readProp() {
var props = PropertiesService.getScriptProperties()
console.log(props.getProperty("NOTION_TOKEN"))
}
If the Token is registered and output correctly, you can delete the above code.
Analyzing the Database
https://www.notion.so/1349f9e927674a03a87d772483dd5b1b?v=05282e02290749fd95decb0df0dc3f5c&p=de3635356a224c569f103a2038dc3521
|--------- DatabaseID ---------| |--------- Page ID ------------|
Opening a database record (page) allows you to obtain the PageID. Use the PageID to acquire the database structure.
function dispoGetDbPgStructure(client, pgId){
const ret = client.pages.retrieve(pgId)
console.log(JSON.stringify(ret))
}
function testMain() {
var props = PropertiesService.getScriptProperties()
const notion = new Notion.client(props.getProperty("NOTION_TOKEN"))
dispoGetDbPgStructure(notion, "8aa1faecaba847f488f277a4b711a42e")
}
If the PageID is set correctly, you will get the database row information in JSON. The properties
in the response contain the column names. Under each column name, there is information like text content, color, and text decoration. For simple data insertion, you can update by using only the necessary information, such as text, so extract only the required parts from the JSON. Use the extracted information as a reference to convert TickTick's JSON to Notion's format.
Date Format Conversion
In TickTick, date-time information is expressed as July 4 2021 at 01:13PM
, and date information as July 4 2021
. In Notion, these are expressed as 2021-07-04T13:13:00.000+09:00
and 2021-07-04
, respectively. Convert the date format from TickTick to Notion accordingly.
function _isDatetime(dtString) {
return (dtString.indexOf("AM") >= 0 || dtString.indexOf("PM") >= 0)
}
function _rmDtNoise(dtString) {
var rmAt = dtString.replace(" at ", " ")
var blAM = rmAt.replace("AM", " AM")
var blPM = blAM.replace("PM", " PM")
return blPM
}
function _formatDate(dt, isDt, timezone="+09:00", sep="-") {
var year = dt.getFullYear()
var month = ("00" + (dt.getMonth()+1)).slice(-2)
var day = ("00" + dt.getDate()).slice(-2)
var date = `${year}${sep}${month}${sep}${day}`
if (!isDt) {
return date;
}
var hour = ("00" + (dt.getHours())).slice(-2)
var min = ("00" + (dt.getMinutes())).slice(-2)
var datetime = `${date}T${hour}:${min}:00.000${timezone}`
return datetime
}
function dtFormatter(dtString) {
var fmtDtString = _rmDtNoise(dtString)
var isDT = _isDatetime(fmtDtString)
var dtParse = Date.parse(fmtDtString)
var dt = new Date(dtParse)
return _formatDate(dt, isDT)
}
To branch processing based on whether it includes date-time information, the _isDatetime
function checks for date-time inclusion. The _rmDtNoise
function removes at
and inserts a half-width space before AM/PM using replace.
Date.parse
acquires the numerical information of the date and passes it to a Date
object to create a Date-type object. The _formatDate
function converts from Date type to String type in Notion's format.
Execute the above series of processes with the dtFormatter
function. The following is code to convert the date format using dtFormatter
. This is for testing, so you can delete it after execution.
function dispoTest(){
var ret1 = dtFormatter("July 4 2021 at 01:13PM")
console.log(ret1) // 2021-07-04T13:13:00.000+09:00
var ret2 = dtFormatter("July 4 2021")
console.log(ret2) // 2021-07-04
}
Tag (MultiSelect) Conversion
The simple structure of Notion's MultiSelect type is as follows.
{
"Tag": {
"multi_select": [
{"name": "Private",},
{"name": "Work", }
]
}
}
}
TickTick's Tag information is in the format #{tagName}
. If there are multiple tags, they are written separated by half-width spaces. Due to this specification, #{tagName}
cannot include #
or half-width spaces. We will write a function to create multi_select
values using this specification.
function genMultiSel(multiSelStr) {
var ret = []
var selList = multiSelStr.split(" ")
selList.forEach((tag) => {
if (tag.length > 0) {
var rmSharp = tag.replace("#", "")
ret.push({name: rmSharp})
}
})
return ret
}
The following is test code.
function test() {
var ret = genMultiSel("#test1 #test2")
console.log(ret)
// [ { name: 'test1' }, { name: 'test2' } ]
}
Generating the Body (JSON)
Create JSON for POSTing to Notion.
function _addDate(params, dateStrings, key, value) {
if (dateStrings.length > 0) {
params["properties"][key] = { "date": { "start": value } }
}
return params
}
function genParams(data, databaseId) {
var params = {
"parent": { "database_id": databaseId },
"properties": {
"TaskName": {
"title": [{ "text": { "content": data.TaskName } }]
},
"TaskContent": {
"rich_text": [{ "text": { "content": data.TaskContent } }]
},
"List": {
"rich_text": [{ "text": { "content": data.List } }]
},
"Priority": {
"rich_text": [{ "text": { "content": data.Priority } }]
},
"LinkToTask": {
"rich_text": [{ "text": { "content": data.LinkToTask } }]
},
"CreatedAt": { "date": { "start": dtFormatter(data.CreatedAt) } }
}
}
var tags = genMultiSel(data.Tag)
if (tags.length > 0) {
params["properties"]["Tag"] = {"multi_select": tags}
}
params = _addDate(params, data.StartDate.length, "StartDate", dtFormatter(data.StartDate))
params = _addDate(params, data.EndDate.length, "EndDate", dtFormatter(data.EndDate))
return params
}
Specify the database using DatabaseID as the destination for data insertion. Set properties
according to the database type. For MultiSelect, add values later based on the presence of tags. This completes the conversion process from TickTick to Notion.
Deploy doPost
Reflect the above processing in doPost.
function doPost(e) {
// Init
var props = PropertiesService.getScriptProperties();
const notion = new Notion.client(props.getProperty("NOTION_TOKEN"));
var jsonString = e.postData.getDataAsString();
var data = JSON.parse(jsonString);
// Generate a Parameter
const params = genParams(data, “cf0bc59e33734173838e0370c210fa3d”)
// Create a record
var ret = notion.pages.create(params)
if (ret[“status”] >= 300 && ret[“status”] < 200 ) {
const options = {name: “IFTTT Webhooks Error”};
GmailApp.sendEmail(“yourAlias@gmail.com”, “webhookTest”, JSON.stringify(ret), options);
}
}
Perform the deployment again to reflect the changes. After deployment, a new URL will be issued, so use that URL to confirm if records are added to the Notion database. If there are error emails or messages, debug according to the message.
IFTTT Settings
Finally, set IFTTT to call the GAS API. For the Trigger, select TickTick's New completed task
. In New completed task, you can select the target List, Tag, or Priority. Since Notion's database allows for aggregation and filtering, set it as follows to stock all tasks.
Item | Value |
---|---|
List | All Lists |
Tag | Please Select(Default) |
Priority | Please Select(Default) |
Click [Create trigger]. For Then That, select Webhook and set as follows.
Item | Value |
---|---|
URL | Deployed GAS API URL |
Method | POST |
Content Type | application/json |
Body | See below |
[Body]
{
"TaskName": "{{TaskName}}",
"TaskContent": "{{TaskContent}}",
"CompleteDate": "{{CompleteDate}}",
"StartDate": "{{StartDate}}",
"EndDate": "{{EndDate}}",
"List": "{{List}}",
"Priority": "{{Priority}}",
"Tag": "{{Tag}}",
"LinkToTask": "{{LinkToTask}}",
"CreatedAt": "{{CreatedAt}}"
}
Click [Create action].
Operation Confirmation
Create a task in TickTick and set it to completed. If the completed task is registered in Notion, it is operating normally. Note that there may be a lag of a few minutes until the event fires in IFTTT, so check the operation status via IFTTT's "View activity", error emails, or the Notion database.
Summary
We have now made it possible to call the Notion API from GAS or IFTTT. This allows for more flexible data stocking using time triggers or event triggers with Notion. It can be widely used for tasks, habits, health management, analysis, etc., so feel free to adapt it to your own purposes.