{ "kind": "open-flow-workflow", "version": 1, "name": "Refresh Daily Todoist Tasks", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_539152b00611e8a6": { "name": "Every day at 05:10 UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "10 5 * * *", "timezone": "UTC" } ] }, "n_6b8c4233adea037b": { "inputs": { "projectId": { "kind": "value", "value": "YOUR_TEMPLATE_PROJECT_ID" }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6b8c4233adea037b", "output": "state" } ] } }, "name": "Read template tasks", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "projectId" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "projectId" } ], "name": "Read template tasks", "moduleId": "n_a7c9d470bfd5e81c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "todoist.list_tasks" } ] } ] } }, "n_c3a12a24829bdb34": { "inputs": {}, "name": "More template pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6b8c4233adea037b", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_72b8035035b24a34": { "inputs": { "projectId": { "kind": "value", "value": "YOUR_INBOX_PROJECT_ID" }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72b8035035b24a34", "output": "state" } ] } }, "name": "Read Inbox tasks", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "projectId" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "projectId" } ], "name": "Read Inbox tasks", "moduleId": "n_3aaec584decf9025", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "todoist.list_tasks" } ] } ] } }, "n_30dffaab5204b2ba": { "inputs": {}, "name": "More Inbox pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_72b8035035b24a34", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_0d7c4ac6967e17fa": { "inputs": { "templates": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6b8c4233adea037b", "output": "state" } ] }, "inbox": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72b8035035b24a34", "output": "state" } ] }, "now": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_539152b00611e8a6", "output": "scheduledAt" } ] }, "templateProject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6b8c4233adea037b", "output": "projectId" } ] }, "inboxProject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72b8035035b24a34", "output": "projectId" } ] } }, "name": "Select old tasks and today’s templates", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "templates" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "inbox" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "now" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "templateProject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "inboxProject" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": {} }, "nullable": false, "handle": "remove" }, { "jsonSchema": { "type": "array", "items": {} }, "nullable": false, "handle": "create" } ], "name": "Select old tasks and today’s templates", "moduleId": "n_69c4061176ca866e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_1da7af5b40f30d73": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0d7c4ac6967e17fa", "output": "remove" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1da7af5b40f30d73", "output": "state" } ] } }, "name": "Prepare next deletion", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": {} }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "name": "Prepare next deletion", "moduleId": "n_d4320ac546777c8e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_1546fdf46b70a7a8": { "inputs": {}, "name": "More tasks to delete?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_1da7af5b40f30d73", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_1d5aba1057c1158e": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0d7c4ac6967e17fa", "output": "create" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1d5aba1057c1158e", "output": "state" } ] } }, "name": "Prepare next task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": {} }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" } ], "name": "Prepare next task", "moduleId": "n_f0ac998d2bc88c2e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_6c665de137512470": { "inputs": {}, "name": "More tasks to create?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_1d5aba1057c1158e", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_d44264390e380271": { "inputs": { "endpoint": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1da7af5b40f30d73", "output": "endpoint" } ] } }, "name": "Delete an old daily task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": [ "string", "number", "boolean", "null" ] } }, "nullable": true, "handle": "query" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Delete an old daily task", "executor": { "providerId": "todoist", "method": "DELETE", "query": { "mode": "object", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "kind": "connector-proxy" } } }, "n_110e13102485f8f2": { "inputs": { "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d44264390e380271", "output": "statusCode" } ] } }, "name": "Check deletion", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "done" } ], "name": "Check deletion", "moduleId": "n_60f728378b94c118", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_7b87ebe09ed68a5c": { "inputs": { "task": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1d5aba1057c1158e", "output": "item" } ] }, "projectId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72b8035035b24a34", "output": "projectId" } ] } }, "name": "Create a daily task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "task" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "projectId" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "task" } ], "name": "Create a daily task", "moduleId": "n_a723b669e3a6322b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "todoist.create_task" } ] } ] } } }, "edges": [ { "source": "n_6b8c4233adea037b", "target": "n_c3a12a24829bdb34" }, { "source": "n_c3a12a24829bdb34", "target": "n_6b8c4233adea037b", "sourceHandle": "yes" }, { "source": "n_72b8035035b24a34", "target": "n_30dffaab5204b2ba" }, { "source": "n_30dffaab5204b2ba", "target": "n_72b8035035b24a34", "sourceHandle": "yes" }, { "source": "n_1da7af5b40f30d73", "target": "n_1546fdf46b70a7a8" }, { "source": "n_1d5aba1057c1158e", "target": "n_6c665de137512470" }, { "source": "n_539152b00611e8a6", "target": "n_6b8c4233adea037b" }, { "source": "n_c3a12a24829bdb34", "target": "n_72b8035035b24a34", "sourceHandle": "otherwise" }, { "source": "n_30dffaab5204b2ba", "target": "n_0d7c4ac6967e17fa", "sourceHandle": "otherwise" }, { "source": "n_0d7c4ac6967e17fa", "target": "n_1da7af5b40f30d73" }, { "source": "n_1546fdf46b70a7a8", "target": "n_d44264390e380271", "sourceHandle": "yes" }, { "source": "n_d44264390e380271", "target": "n_110e13102485f8f2" }, { "source": "n_110e13102485f8f2", "target": "n_1da7af5b40f30d73" }, { "source": "n_1546fdf46b70a7a8", "target": "n_1d5aba1057c1158e", "sourceHandle": "otherwise" }, { "source": "n_6c665de137512470", "target": "n_7b87ebe09ed68a5c", "sourceHandle": "yes" }, { "source": "n_7b87ebe09ed68a5c", "target": "n_1d5aba1057c1158e" } ] } }, "modules": { "n_a7c9d470bfd5e81c": { "name": "Read template tasks", "imports": [], "source": "export default async function collect({projectId,previous},context){\n if(!projectId||projectId.startsWith('YOUR_'))throw Error('Configure the Todoist project ID');\n const s=previous??{tasks:[],cursor:null,seen:[]};\n const page=await context.actions.call('todoist.list_tasks',{projectId,limit:200,...(s.cursor?{cursor:s.cursor}:{})});\n if(!Array.isArray(page.tasks))throw Error('Invalid Todoist task page');\n const cursor=page.nextCursor;if(cursor!==null&&(typeof cursor!=='string'||!cursor||s.seen.includes(cursor)))throw Error('Invalid or repeated Todoist cursor');\n const state={tasks:[...s.tasks,...page.tasks],cursor,seen:cursor?[...s.seen,cursor]:s.seen};return {state,more:cursor!==null,projectId};\n}\n" }, "n_3aaec584decf9025": { "name": "Read Inbox tasks", "imports": [], "source": "export default async function collect({projectId,previous},context){\n if(!projectId||projectId.startsWith('YOUR_'))throw Error('Configure the Todoist project ID');\n const s=previous??{tasks:[],cursor:null,seen:[]};\n const page=await context.actions.call('todoist.list_tasks',{projectId,limit:200,...(s.cursor?{cursor:s.cursor}:{})});\n if(!Array.isArray(page.tasks))throw Error('Invalid Todoist task page');\n const cursor=page.nextCursor;if(cursor!==null&&(typeof cursor!=='string'||!cursor||s.seen.includes(cursor)))throw Error('Invalid or repeated Todoist cursor');\n const state={tasks:[...s.tasks,...page.tasks],cursor,seen:cursor?[...s.seen,cursor]:s.seen};return {state,more:cursor!==null,projectId};\n}\n" }, "n_69c4061176ca866e": { "name": "Select old tasks and today’s templates", "imports": [], "source": "export default function plan({templates,inbox,now,templateProject,inboxProject}){\n if(!templateProject||!inboxProject||templateProject===inboxProject)throw Error('Template and Inbox projects must differ');\n const date=new Date(now);if(!Number.isFinite(date.getTime()))throw Error('Invalid run date');\n const weekday=['sun','mon','tues','wed','thurs','fri','sat'][date.getUTCDay()];\n const remove=[];for(const t of inbox.tasks){if(!t||typeof t.id!=='string'||!t.id||!Array.isArray(t.labels))throw Error('Invalid Inbox task');if(t.labels.includes('daily'))remove.push(t.id)}\n const create=[];for(const t of templates.tasks){if(!t||typeof t.id!=='string'||!t.id||typeof t.content!=='string'||!t.content||typeof t.description!=='string')throw Error('Invalid template task');const fields={};for(const part of t.description.split(';')){const p=part.trim().split(':');if(p.length===2)fields[p[0]]=p[1].trim()}if(!(fields.days||'').includes(weekday))continue;let dueDatetime;if(fields.due){const m=/^(\\d{1,2})(?:\\.(\\d{2}))?([ap]m)$/i.exec(fields.due);if(!m||+m[1]<1||+m[1]>12||+(m[2]||0)>59)throw Error('Invalid template due time');let h=(+m[1]%12)+(m[3].toLowerCase()==='pm'?12:0);const d=new Date(date);d.setUTCHours(h,+(m[2]||0),0,0);dueDatetime=d.toISOString()}create.push({content:t.content,description:t.description,...(dueDatetime?{dueDatetime}:{})})}\n return {remove,create};\n}\n" }, "n_d4320ac546777c8e": { "name": "Prepare next deletion", "imports": [], "source": "export default function next({items,previous}){const i=previous?.index??0;if(i>=items.length)return {state:{index:i},more:false,item:{},endpoint:'/tasks/-'};const item=typeof items[i]==='string'?{id:items[i]}:items[i];return {state:{index:i+1},more:true,item,endpoint:item.id?'/tasks/'+encodeURIComponent(item.id):'/tasks/-'}}\n" }, "n_f0ac998d2bc88c2e": { "name": "Prepare next task", "imports": [], "source": "export default function next({items,previous}){const i=previous?.index??0;if(i>=items.length)return {state:{index:i},more:false,item:{},endpoint:'/tasks/-'};const item=typeof items[i]==='string'?{id:items[i]}:items[i];return {state:{index:i+1},more:true,item,endpoint:item.id?'/tasks/'+encodeURIComponent(item.id):'/tasks/-'}}\n" }, "n_60f728378b94c118": { "name": "Check deletion", "imports": [], "source": "export default function check({statusCode}){if(statusCode!==204)throw Error('Todoist deletion failed');return {done:true}}\n" }, "n_a723b669e3a6322b": { "name": "Create a daily task", "imports": [], "source": "export default async function create({task,projectId},context){if(!projectId||projectId.startsWith('YOUR_'))throw Error('Configure Inbox project ID');const result=await context.actions.call('todoist.create_task',{...task,projectId,labels:['daily']});if(!result||typeof result.task!=='object'||result.task===null||Array.isArray(result.task)||typeof result.task.id!=='string'||!result.task.id)throw Error('Todoist did not confirm the created task');return {task:result.task}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }