// import { Low, JSONFile } from 'lowdb'; import dotenv from 'dotenv'; dotenv.config(); import { Client as NotionClient } from '@notionhq/client'; import { Filter, RichText } from '@notionhq/client/build/src/api-types'; import { CreateText, GetAllTexts, UpdateCard, UpdateText } from './MiroHelper.js'; import { isEmptyOrSpaces, MiroSyncInfo, MiroWidgetInfo, ProcessMiroWidget, ProcessNotionBlock, ProcessNotionProperty, sleep } from './Utility.js'; // const adapter = new JSONFile('db.json') // const db = new Low(adapter); // Read data from JSON file, this will set db.data content // (async () => await db.read())(); // db.data = db.data || { syncInfo: [] }; // (async () => await db.write())();//if db.json doesn't exist, write file will silent fail at program end let miro_app_id = process.env.MIRO_APP_ID || ""; let notion_page = process.env.NOTION_PAGE || ""; let notion_token = process.env.NOTION_TOKEN || ""; let database_sync_page_count: Number = Number(process.env.DATABASE_SYNC_PAGE_COUNT) || 0; let synced_property_arr = (process.env.SYNCED_PROPERTY || "").split(','); let filter = (process.env.FILTER || ""); let verbose: Number = Number(process.env.VERBOSE) || 0; console.log(synced_property_arr); const notion = new NotionClient({ auth: notion_token }) const PrettyText = (textArr: RichText[]) => { let str = textArr.reduce((all, cur) => `${all}${cur.plain_text}`, ''); return `${str}`; } let counter = 0; let timestamp = 0; let remaining = 0; function MiroRateLimitCallback(rateLimitReset: number, rateLimitRemaining: number) { timestamp = rateLimitReset; remaining = rateLimitRemaining; } function GetTimer() { return Date.now() / 1000 - timestamp; } function ShouldIgnore(str: string) { return isEmptyOrSpaces(str) || str == "

"; } let x = 0; let y = 0; const COLUME_WIDTH = 200; const PART_OFFSET = 1000; function NewRow() { y += 120; } function ResetRow() { y = 0; } function NewColume() { x += COLUME_WIDTH; } function ResetColume() { x = 0; } async function SyncNotion2Miro(notionInfo: NotionInfoType, miroInfo?: MiroSyncInfo, onCreated?: () => void) { if (ShouldIgnore(notionInfo.text)) { console.log(`\tskipping empty string block\n`); return; } counter++; await sleep(100); if (counter % 10 == 0) { console.log('sleep to prevent miro blocking'); await sleep(2000); } while (remaining <= 1000 && GetTimer() < 0) { console.log(`sleep to prevent miro blocking, timer[${GetTimer().toFixed()}]`); await sleep(1000); } console.log(`miro sync counter[${counter}] timer[${GetTimer().toFixed()}] remaining points[${remaining}]`); if (miroInfo == undefined) return; let notionPageId = notionInfo.pageId; let notionPropertyId = notionInfo.propertyId; let info = undefined; if (notionPropertyId === undefined) info = miroInfo[notionPageId] && miroInfo[notionPageId]["_"]; else info = miroInfo[notionPageId] && miroInfo[notionPageId][notionPropertyId]; async function CreateOrUpdateInfo(info?: MiroWidgetInfo) { if (info === undefined) { console.log(`\tcreate text: ${notionInfo.text}\n`); // CreateCard(notionInfo.text, { notionId: notionInfo.id }); await CreateText(notionInfo.text, { notionPageId, notionPropertyId }, x, y, MiroRateLimitCallback); // db.data?.syncInfo.push({ notionId: id }); } else { switch (info.type) {//conversion? case 'card': console.log(`\tupdate miro card[${info.id}] ${notionInfo.text}\n`); await UpdateCard(info.id, notionInfo.text, undefined, MiroRateLimitCallback); break; case 'text': console.log(`\tupdate miro text[${info.id}] ${notionInfo.text}\n`); await UpdateText(info.id, notionInfo.text, undefined, MiroRateLimitCallback); } } } for (let index = 0; index < 2; index++) { if (info === undefined) { await CreateOrUpdateInfo(); onCreated && onCreated(); if (index == 0) x += PART_OFFSET; else x -= PART_OFFSET + COLUME_WIDTH; } else await CreateOrUpdateInfo(info[index]); } } // async function SyncNotionPage(pageId: string, miroWidgetInfo: MiroSyncInfo) { // let blocks = await notion.blocks.children.list({ block_id: pageId }); // console.log(blocks); // //TODO deal with pagination // if (blocks == undefined) return; // if (miroWidgetInfo == undefined) return; // if (blocks.has_more) // console.warn("Need to deal with paging results!!"); // if (verbose) // console.log(JSON.stringify(blocks.results, null, "\t")); // let processedBlocks = ProcessNotionBlock(blocks.results); // for (var block of processedBlocks) { // if (block == null) continue; // if (block.richText == null) { // if (block.id == null) continue; // console.warn(`try to handle unsupported block[${block.id}]`); // await SyncNotionDatabase(block.id, miroWidgetInfo); // continue; // } // let text = PrettyText(block.richText); // console.log(`block[${block.id}] notionInfo.text`); // await SyncNotion2Miro({ pageId: block.id, text: `

${text}

` }, miroWidgetInfo); // } // } async function SyncNotionDatabase(databaseId: string, miroWidgetInfo?: MiroSyncInfo) { try { let db = await notion.databases.retrieve({ database_id: databaseId }); console.log(`Processing Database: ${PrettyText(db.title)}`); let _filter = JSON.parse(filter) as Filter; console.log(filter); let pages = await notion.databases.query({ database_id: databaseId, filter: _filter }); //TODO deal with pagination if (pages == undefined) return; if (miroWidgetInfo == undefined) return; let count = 0; for (let page of pages.results) { let processedProperties = ProcessNotionProperty(Object.entries(page.properties)); for (let property of processedProperties) { if (property == null) continue; if (synced_property_arr.includes(property.name) == false) continue; if (database_sync_page_count != 0 && count >= database_sync_page_count) continue; count++; let text = property.richText ? PrettyText(property.richText) : property.text; text = text || ""; console.log(`property[${property.id}] ${text}`); let notionInfo = { pageId: page.id, propertyId: property.id, text: `

${text}

` }; if (ShouldIgnore(text)) NewColume(); await SyncNotion2Miro(notionInfo, miroWidgetInfo, NewColume); } ResetColume(); NewRow(); } } catch (err) { } finally { return []; } } (async () => { try { let collection = await GetAllTexts(); let info = ProcessMiroWidget(collection.data, miro_app_id); // console.log(JSON.stringify(collection)); await SyncNotionDatabase(notion_page, info); // await SyncNotionPage(notion_page, info); // await db.write(); } catch (error) { console.log(error); } })(); type NotionInfoType = { pageId: string, propertyId?: string, text: string }