Lorem ipsum content with an image and a random file below
import process from "node:process";
import { TodoistApi } from "npm:@doist/todoist-api-typescript";
import { Client } from "npm:@notionhq/client";
const TODOIST_API_KEY = process.env.TODOIST_API_KEY;
const todoistapi = new TodoistApi(TODOIST_API_KEY);
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const notion = new Client({
auth: NOTION_API_KEY,
});
var add_to_notion_todoist_project_id = "PROJECT_ID_HERE";
var todoist_dict_mapping = {
"habit": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "page",
"notion-id": "PAGE_ID_HERE",
},
"papers": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "database",
"notion-id": "DB_ID_HERE",
},
};
function getNotionId(section_id) {
if (!section_id) {
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
for (var key in todoist_dict_mapping) {
if (todoist_dict_mapping[key]["todoist-section-id"] === section_id) {
return [
todoist_dict_mapping[key]["notion-map-type"] || todoist_dict_mapping["dump"]["notion-map-type"],
todoist_dict_mapping[key]["notion-id"] || todoist_dict_mapping["dump"]["notion-id"],
];
}
}
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
function convertDateObject(due) {
function convertToISOWithOffset(datetimeStr, timezoneStr) {
const date = new Date(datetimeStr);
const [, sign, hours, minutes] = timezoneStr.match(/GMT ([+-])(\d{1,2}):(\d{2})/);
date.setUTCMinutes(date.getUTCMinutes() + (parseInt(hours) * 60 + parseInt(minutes)) * (sign === "+" ? 1 : -1));
return date.toISOString().split(".")[0]
+ `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
}
const formatDate = (date, datetime, timezone) => {
let isoString = datetime ? datetime : date;
if (timezone && timezone.startsWith("GMT") && timezone.length > 3) {
return convertToISOWithOffset(datetime, timezone);
} else {
return isoString;
}
};
return {
start: due ? formatDate(due.date, due.datetime, due.timezone) : new Date().toISOString(),
end: null,
time_zone: due && due.datetime && due.timezone && due.timezone.startsWith("GMT") && due.timezone.length > 3
? null
: (due && due.datetime && due.timezone ? due.timezone : "America/Los_Angeles"),
};
}
async function addCalloutToNotionPage(page_id, content, date) {
console.log(JSON.stringify(date));
const response = await notion.blocks.children.append({
block_id: page_id,
children: [{
"callout": {
"rich_text": [{
"type": "mention",
"mention": {
"type": "date",
"date": date,
},
}],
"icon": {
"type": "external",
"external": {
"url": "https://www.notion.so/icons/circle-dot_lightgray.svg",
},
},
"children": [{
"paragraph": {
"rich_text": [{
"text": {
"content": content,
},
}],
},
}],
},
}],
});
console.log(JSON.stringify(response));
}
async function addPageToNotionDatabse(database_id, content) {
const response = await notion.pages.create({
"parent": {
"type": "database_id",
"database_id": database_id,
},
"properties": {
"Name": {
"title": [{
"text": {
"content": content,
},
}],
},
},
});
}
export default async function(interval: Interval) {
var tasks = await todoistapi.getTasks({
projectId: add_to_notion_todoist_project_id,
});
for (const task of tasks) {
console.log(task);
const [mappedNotionType, mappedNotionId] = getNotionId(task.sectionId);
if (mappedNotionId)
{
if (mappedNotionType == "page" && mappedNotionId) {
addCalloutToNotionPage(mappedNotionId, task.content, convertDateObject(task.due));
}
else if (mappedNotionType == "database" && mappedNotionId) {
addPageToNotionDatabse(mappedNotionId, task.content);
}
todoistapi.deleteTask(task.id);
}
}
}
Remember this code is in format of val town and hence the weird imports. You would need to switch that to require probably (I ain’t good at JS)
Code in case you want to host somewhere else
import process from "node:process";
import { TodoistApi } from "npm:@doist/todoist-api-typescript";
import { Client } from "npm:@notionhq/client";
const TODOIST_API_KEY = process.env.TODOIST_API_KEY;
const todoistapi = new TodoistApi(TODOIST_API_KEY);
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const notion = new Client({
auth: NOTION_API_KEY,
});
var add_to_notion_todoist_project_id = "PROJECT_ID_HERE";
var todoist_dict_mapping = {
"habit": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "page",
"notion-id": "PAGE_ID_HERE",
},
"papers": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "database",
"notion-id": "DB_ID_HERE",
},
};
function getNotionId(section_id) {
if (!section_id) {
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
for (var key in todoist_dict_mapping) {
if (todoist_dict_mapping[key]["todoist-section-id"] === section_id) {
return [
todoist_dict_mapping[key]["notion-map-type"] || todoist_dict_mapping["dump"]["notion-map-type"],
todoist_dict_mapping[key]["notion-id"] || todoist_dict_mapping["dump"]["notion-id"],
];
}
}
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
function convertDateObject(due) {
function convertToISOWithOffset(datetimeStr, timezoneStr) {
const date = new Date(datetimeStr);
const [, sign, hours, minutes] = timezoneStr.match(/GMT ([+-])(\d{1,2}):(\d{2})/);
date.setUTCMinutes(date.getUTCMinutes() + (parseInt(hours) * 60 + parseInt(minutes)) * (sign === "+" ? 1 : -1));
return date.toISOString().split(".")[0]
+ `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
}
const formatDate = (date, datetime, timezone) => {
let isoString = datetime ? datetime : date;
if (timezone && timezone.startsWith("GMT") && timezone.length > 3) {
return convertToISOWithOffset(datetime, timezone);
} else {
return isoString;
}
};
return {
start: due ? formatDate(due.date, due.datetime, due.timezone) : new Date().toISOString(),
end: null,
time_zone: due && due.datetime && due.timezone && due.timezone.startsWith("GMT") && due.timezone.length > 3
? null
: (due && due.datetime && due.timezone ? due.timezone : "America/Los_Angeles"),
};
}
async function addCalloutToNotionPage(page_id, content, date) {
console.log(JSON.stringify(date));
const response = await notion.blocks.children.append({
block_id: page_id,
children: [{
"callout": {
"rich_text": [{
"type": "mention",
"mention": {
"type": "date",
"date": date,
},
}],
"icon": {
"type": "external",
"external": {
"url": "https://www.notion.so/icons/circle-dot_lightgray.svg",
},
},
"children": [{
"paragraph": {
"rich_text": [{
"text": {
"content": content,
},
}],
},
}],
},
}],
});
console.log(JSON.stringify(response));
}
async function addPageToNotionDatabse(database_id, content) {
const response = await notion.pages.create({
"parent": {
"type": "database_id",
"database_id": database_id,
},
"properties": {
"Name": {
"title": [{
"text": {
"content": content,
},
}],
},
},
});
}
export default async function(interval: Interval) {
var tasks = await todoistapi.getTasks({
projectId: add_to_notion_todoist_project_id,
});
for (const task of tasks) {
console.log(task);
const [mappedNotionType, mappedNotionId] = getNotionId(task.sectionId);
if (mappedNotionId)
{
if (mappedNotionType == "page" && mappedNotionId) {
addCalloutToNotionPage(mappedNotionId, task.content, convertDateObject(task.due));
}
else if (mappedNotionType == "database" && mappedNotionId) {
addPageToNotionDatabse(mappedNotionId, task.content);
}
todoistapi.deleteTask(task.id);
}
}
}
Remember this code is in format of val town and hence the weird imports. You would need to switch that to require probably (I ain’t good at JS)
sample2.html
Download