{ "kind": "open-flow-workflow", "version": 1, "name": "Weekly Matrix Coffee Groups", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_473ee16f07b5623e": { "name": "Mondays at 10:00 UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 10 * * 1", "timezone": "UTC" } ] }, "n_db9f255db0cf7cdf": { "inputs": { "homeserver": { "kind": "value", "value": "https://YOUR_MATRIX_HOMESERVER" }, "memberRoom": { "kind": "value", "value": "!YOUR_MEMBER_ROOM:example.org" }, "announcementRoom": { "kind": "value", "value": "!YOUR_ANNOUNCEMENT_ROOM:example.org" } }, "name": "Configure the Matrix rooms", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "homeserver" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "memberRoom" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "announcementRoom" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "base" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "memberRoom" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "announcementRoom" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "greetingUrl" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "membersUrl" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "greeting" } ], "name": "Configure the Matrix rooms", "moduleId": "n_ba7937cada5371b9", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_4e3e202a91351afb": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_db9f255db0cf7cdf", "output": "greetingUrl" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_db9f255db0cf7cdf", "output": "greeting" } ] }, "auth.token": { "kind": "unset" } }, "name": "Post the coffee greeting", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "auth.token" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Post the coffee greeting", "executor": { "method": "PUT", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "authentication": { "type": "bearer" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_ba90a055ed9dd878": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4e3e202a91351afb", "output": "body" } ] } }, "name": "Confirm the greeting", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "confirmed" } ], "name": "Confirm the greeting", "moduleId": "n_328ae71facabfcb2", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9367721e0613b672": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_db9f255db0cf7cdf", "output": "membersUrl" } ] }, "auth.token": { "kind": "unset" } }, "name": "Read room membership events", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "auth.token" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Read room membership events", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "bearer" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_34082e42b1cadf9d": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9367721e0613b672", "output": "body" } ] } }, "name": "Shuffle and form coffee groups", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }, "nullable": false, "handle": "groups" } ], "name": "Shuffle and form coffee groups", "moduleId": "n_1abe4bb131b04086", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_250d9e8bf65860f0": { "inputs": { "groups": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_34082e42b1cadf9d", "output": "groups" } ] }, "base": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_db9f255db0cf7cdf", "output": "base" } ] }, "announcementRoom": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_db9f255db0cf7cdf", "output": "announcementRoom" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_250d9e8bf65860f0", "output": "state" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_708e718733d254ae", "output": "body" } ] } }, "name": "Prepare the next group message", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }, "nullable": false, "handle": "groups" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "base" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "announcementRoom" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "message" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" } ], "name": "Prepare the next group message", "moduleId": "n_9eff99c182ec3a8a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_741df6ff7acbfe1e": { "inputs": {}, "name": "More groups to announce?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_250d9e8bf65860f0", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_708e718733d254ae": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_250d9e8bf65860f0", "output": "url" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_250d9e8bf65860f0", "output": "message" } ] }, "auth.token": { "kind": "unset" } }, "name": "Announce the coffee group", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "auth.token" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Announce the coffee group", "executor": { "method": "PUT", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "authentication": { "type": "bearer" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_4a971a55204e9048": { "name": "Group count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_250d9e8bf65860f0", "output": "count" } ] } } } }, "edges": [ { "source": "n_473ee16f07b5623e", "target": "n_db9f255db0cf7cdf" }, { "source": "n_db9f255db0cf7cdf", "target": "n_4e3e202a91351afb" }, { "source": "n_4e3e202a91351afb", "target": "n_ba90a055ed9dd878" }, { "source": "n_ba90a055ed9dd878", "target": "n_9367721e0613b672" }, { "source": "n_9367721e0613b672", "target": "n_34082e42b1cadf9d" }, { "source": "n_34082e42b1cadf9d", "target": "n_250d9e8bf65860f0" }, { "source": "n_250d9e8bf65860f0", "target": "n_741df6ff7acbfe1e" }, { "source": "n_741df6ff7acbfe1e", "target": "n_708e718733d254ae", "sourceHandle": "more" }, { "source": "n_708e718733d254ae", "target": "n_250d9e8bf65860f0" }, { "source": "n_741df6ff7acbfe1e", "target": "n_4a971a55204e9048", "sourceHandle": "otherwise" } ] } }, "modules": { "n_ba7937cada5371b9": { "name": "Configure the Matrix rooms", "imports": [], "source": "export default function config({homeserver,memberRoom,announcementRoom}){if(typeof homeserver!=='string'||homeserver.includes('YOUR_')||!/^https:\\/\\/[^/?#@\\s]+(?:\\/[^?#\\s]*)?$/i.test(homeserver))throw Error('Configure an HTTPS Matrix homeserver without credentials');for(const room of [memberRoom,announcementRoom])if(typeof room!=='string'||room.includes('YOUR_')||!/^![^\\s/:]+:[^\\s/]+$/.test(room))throw Error('Configure both Matrix room IDs');const base=homeserver.replace(/\\/+$/,''),prefix=base+'/_matrix/client/v3/rooms/',txn=Date.now().toString(36)+'-'+Math.random().toString(36).slice(2)+'-'+Math.random().toString(36).slice(2);return{base,memberRoom,announcementRoom,greetingUrl:prefix+encodeURIComponent(memberRoom)+'/send/m.room.message/'+txn,membersUrl:prefix+encodeURIComponent(memberRoom)+'/members',greeting:{msgtype:'m.text',body:\"👋 Happy Monday Groups for this week's virtual coffee are:\"}}}\n" }, "n_328ae71facabfcb2": { "name": "Confirm the greeting", "imports": [], "source": "export default function greeting({body}){if(typeof body?.event_id!=='string'||!body.event_id)throw Error('Matrix did not confirm the greeting');return{confirmed:true}}\n" }, "n_1abe4bb131b04086": { "name": "Shuffle and form coffee groups", "imports": [], "source": "export default function groups({body}){if(!Array.isArray(body?.chunk))throw Error('Matrix returned no member list');const users=body.chunk.map(e=>{if(e?.type!=='m.room.member'||typeof e.state_key!=='string'||!/^@[^\\s:]+:[^\\s]+$/.test(e.state_key))throw Error('Invalid Matrix member event');return e.state_key});if(new Set(users).size!==users.length)throw Error('Duplicate Matrix member');if(users.length<2)return{groups:[]};for(let n=users.length;n>0;n--){const j=Math.floor(Math.random()*n);[users[n-1],users[j]]=[users[j],users[n-1]]}const groups=[];for(let i=0;i=groups.length)return{state,more:false,url:'https://example.com',message:{},count:groups.length};const txn=Date.now().toString(36)+'-'+Math.random().toString(36).slice(2)+'-'+Math.random().toString(36).slice(2)+'-'+state.index;return{state,more:true,url:base+'/_matrix/client/v3/rooms/'+encodeURIComponent(announcementRoom)+'/send/m.room.message/'+txn,message:{msgtype:'m.text',body:'☀️ '+groups[state.index].join(', ')},count:groups.length}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }