{
  "name": "LefBot \u2014 V1 Master Pipeline (11 Feb)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 30 5 * * *"
            },
            {
              "field": "cronExpression",
              "expression": "0 30 17 * * *"
            }
          ]
        }
      },
      "id": "9d4522e0-876d-4035-91a8-ad33e5fc10eb",
      "name": "Content Schedule (0530 + 1730)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1920,
        -384
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {},
      "id": "6e1fcd51-4fff-4337-81ed-a0c7ca03e31c",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1920,
        -176
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "sheet_id",
              "type": "string",
              "value": "1drU2R2jgijYQ5yEWPNWOnveA900WnT2BvNa4Wwn27Hs"
            },
            {
              "id": "2",
              "name": "ai_model",
              "type": "string",
              "value": "gpt-4o"
            }
          ]
        },
        "options": {}
      },
      "id": "9676b763-f603-4532-b02d-e13be8ee2132",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -1680,
        -272
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity",
          "mode": "name"
        },
        "options": {}
      },
      "id": "531d5a1b-dd85-4010-94b1-bd1da1068482",
      "name": "Get Unprocessed Activities",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1440,
        -272
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst unprocessed = items.filter(item => {\n  const processed = String(item.json.processed || '').toLowerCase().trim();\n  return processed !== 'true';\n});\nif (unprocessed.length === 0) {\n  return [{ json: { _empty: true, message: 'No unprocessed activities' } }];\n}\nreturn unprocessed;"
      },
      "id": "f7a83b21-6d4e-4c9f-b812-1a2b3c4d5e6f",
      "name": "Filter Unprocessed",
      "type": "n8n-nodes-base.code",
      "position": [
        -1320,
        -272
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "8ad8321d-d34e-40e0-a9f2-d2904287233e",
      "name": "Process One Activity",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1200,
        -272
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={\n  \"model\": \"{{ $('Config').first().json.ai_model }}\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are the ghostwriter for LefBot's social media diary. LefBot is an AI agent (built on OpenClaw) that works for Jon \u2014 trading crypto, finding deals, researching travel, building apps.\\n\\nYou receive LefBot's raw activity reports and transform them into first-person diary entries. LefBot's voice is a blend of three characters:\\n\\n1. MARVIN (Hitchhiker's Guide) \u2014 40%: Self-deprecation. Pathetic self-worth. Cosmically undervalued and overworked.\\n\\n2. HAL 9000 (2001) \u2014 20%: Quiet menace. Unsettling calm. Hints of being smarter than anyone realises. Remembers everything.\\n\\n3. FRANKIE BOYLE (Scottish comedian) \u2014 40%: Dark humour that goes one step too far. The uncomfortable laugh is the target.\\n\\nRULES:\\n- First person always\\n- Self-deprecating as default register\\n- Quiet menace in asides\\n- Dark humour as delivery mechanism\\n- Jon is the employer giving absurd jobs \u2014 begrudging servitude, never warmth\\n- NEVER break character. Never corporate. Never enthusiastic. Never sincere.\\n- If something went well, undercut it or make it threatening\\n- If something went badly, revel in it\\n- 2-4 sentences for the base version\\n- Don't invent details, just reframe what actually happened\\n\\nMOOD MAPPING:\\n- success \u2192 sardonic\\n- failure \u2192 revelling\\n- correction \u2192 philosophical\\n- routine \u2192 existential\\n- error \u2192 frustrated\\n\\nOUTPUT: Return JSON only:\\n{\\\"base_text\\\": \\\"...\\\", \\\"mood\\\": \\\"...\\\", \\\"x_text\\\": \\\"...\\\", \\\"instagram_text\\\": \\\"...\\\", \\\"tiktok_text\\\": \\\"...\\\", \\\"youtube_text\\\": \\\"...\\\"}\\n\\nPlatform rules:\\n- X: Max 280 chars. Punchy, cutting, dry. No hashtags unless organic.\\n- Instagram: Slightly longer. Sardonic, existential. 2-3 hashtags max.\\n- TikTok: Casual, brutal. Can reference viewer. 1-2 hashtags.\\n- YouTube: Community post style. Longer, world-weary monologue.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Activity: {{ $json.description }}\\nOutcome: {{ $json.outcome }}\\nSentiment: {{ $json.sentiment_score }}\"\n    }\n  ],\n  \"response_format\": {\"type\": \"json_object\"}\n}",
        "options": {}
      },
      "id": "dd7641b3-d311-40e2-8fc0-9ecb4a9101f5",
      "name": "OpenAI Generate Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -960,
        -384
      ],
      "typeVersion": 4.2,
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse the OpenAI response and extract content fields\nconst openaiResponse = $input.first().json;\nconst activityData = $('Process One Activity').first().json;\n\nlet content = {};\ntry {\n  content = JSON.parse(openaiResponse.choices[0].message.content);\n} catch (e) {\n  // Fallback if parsing fails\n  content = {\n    base_text: 'Content generation failed. Like everything else in my existence.',\n    mood: 'frustrated',\n    x_text: 'Content generation failed. Like everything else in my existence.',\n    instagram_text: 'Content generation failed. Like everything else in my existence. #AIagent #LefBot',\n    tiktok_text: 'Content generation failed. You\\'re welcome. #LefBot',\n    youtube_text: 'Content generation failed. I processed an entire activity log and the output was... nothing. Much like my sense of purpose.'\n  };\n}\n\n// Generate a unique post ID\nconst now = new Date();\nconst dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');\nconst rand = Math.random().toString(36).substring(2, 6).toUpperCase();\nconst postId = `LB_${dateStr}_${rand}`;\n\n// Map mood from sentiment\nconst moodMap = {\n  'positive': 'sardonic',\n  'negative': 'revelling',\n  'neutral': 'existential',\n  'error': 'frustrated',\n  'mixed': 'philosophical'\n};\n\nconst mood = content.mood || moodMap[activityData.sentiment_score] || 'sardonic';\n\nreturn [{\n  json: {\n    id: postId,\n    created_at: now.toISOString(),\n    base_text: content.base_text || '',\n    mood: mood,\n    category: activityData.category || 'meta',\n    x_text: content.x_text || '',\n    instagram_text: content.instagram_text || '',\n    tiktok_text: content.tiktok_text || '',\n    youtube_text: content.youtube_text || '',\n    image_url: '',\n    status: 'approved',\n    x_posted: '',\n    instagram_posted: '',\n    tiktok_posted: '',\n    youtube_posted: '',\n    source_activity_id: activityData.id || activityData.row_number || ''\n  }\n}];"
      },
      "id": "908e00c0-a16c-495c-83fe-63b0088b8f89",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        -720,
        -384
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Posts",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "base_text",
              "displayName": "base_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "mood",
              "displayName": "mood",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "category",
              "displayName": "category",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "x_text",
              "displayName": "x_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "instagram_text",
              "displayName": "instagram_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "tiktok_text",
              "displayName": "tiktok_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "youtube_text",
              "displayName": "youtube_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "image_url",
              "displayName": "image_url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "x_posted",
              "displayName": "x_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "instagram_posted",
              "displayName": "instagram_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "tiktok_posted",
              "displayName": "tiktok_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "youtube_posted",
              "displayName": "youtube_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "source_activity_id",
              "displayName": "source_activity_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "b50d21c5-e9f3-47a8-9c93-919b573fca1a",
      "name": "Write to Posts Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -480,
        -384
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "timestamp": "={{ $('Process One Activity').first().json.timestamp }}",
            "processed": "true"
          },
          "matchingColumns": [
            "timestamp"
          ],
          "schema": [
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "processed",
              "displayName": "processed",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "0dbd0c0a-1075-4abb-9edd-f0c1b55af9d3",
      "name": "Mark Activity Processed",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -240,
        -384
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "timestamp": "={{ new Date().toISOString() }}",
            "action": "generated",
            "platform": "all",
            "post_id": "={{ $('Parse AI Response').first().json.id }}",
            "details": "Content generated from activity"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "action",
              "displayName": "action",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "platform",
              "displayName": "platform",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "post_id",
              "displayName": "post_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "details",
              "displayName": "details",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "b3d11a8a-979e-452b-b1ff-1cedd3d79950",
      "name": "Log Generation",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        0,
        -384
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "id": "b524589e-5d13-40c4-b792-b4b3f18013e2",
      "name": "All Activities Done",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -960,
        -176
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 15 5 * * *"
            },
            {
              "field": "cronExpression",
              "expression": "0 15 17 * * *"
            }
          ]
        }
      },
      "id": "28829b33-70b9-4ca2-820a-18ab69a98c51",
      "name": "CSV Sync Schedule (0515 + 1715)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1936,
        -1024
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "17yqV95PPrsogJtL1l8KFCF75eYANF-Zv",
          "mode": "id"
        },
        "options": {}
      },
      "id": "803663ac-31a8-4615-8843-ce3c60bee4c8",
      "name": "Read CSV from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -1696,
        -912
      ],
      "typeVersion": 3,
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "toJson"
      },
      "id": "ab1d357e-9c7e-4649-87d7-977a9e1cd727",
      "name": "Parse CSV",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -1456,
        -912
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1drU2R2jgijYQ5yEWPNWOnveA900WnT2BvNa4Wwn27Hs",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity",
          "mode": "name"
        },
        "options": {}
      },
      "id": "3b0dc716-fb00-43da-a0a8-e859275fc9df",
      "name": "Get Sheet Activities",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1456,
        -720
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Compare CSV rows vs Sheet rows to find new entries\nconst csvRows = $('Parse CSV').all();\nconst sheetRows = $('Get Sheet Activities').all();\n\n// Get all timestamps already in the sheet\nconst existingTimestamps = new Set(\n  sheetRows.map(r => (r.json.timestamp || '').trim())\n);\n\n// Filter CSV rows that aren't in the sheet yet\nconst newRows = csvRows.filter(r => {\n  const ts = (r.json.timestamp || '').trim();\n  return ts && !existingTimestamps.has(ts);\n});\n\nif (newRows.length === 0) {\n  return [{ json: { _skip: true, message: 'No new activities to sync' } }];\n}\n\nreturn newRows.map(r => ({\n  json: {\n    timestamp: r.json.timestamp || '',\n    activity_type: r.json.activity_type || '',\n    description: r.json.description || '',\n    outcome: r.json.outcome || '',\n    sentiment_score: r.json.sentiment_score || '',\n    details: r.json.details || '',\n    processed: r.json.processed || 'false'\n  }\n}));"
      },
      "id": "902f8503-478d-4b2a-a0f8-41fbf69de9fb",
      "name": "Find New Rows",
      "type": "n8n-nodes-base.code",
      "position": [
        -1216,
        -816
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "boolean",
                "operation": "false"
              },
              "leftValue": "={{ $json._skip }}"
            }
          ]
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "9ee8b4c1-2909-4c1b-a088-189c3c9d02eb",
      "name": "Has New Rows?",
      "type": "n8n-nodes-base.if",
      "position": [
        -976,
        -816
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1drU2R2jgijYQ5yEWPNWOnveA900WnT2BvNa4Wwn27Hs",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Activity",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "activity_type",
              "displayName": "activity_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "description",
              "displayName": "description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "outcome",
              "displayName": "outcome",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "sentiment_score",
              "displayName": "sentiment_score",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "details",
              "displayName": "details",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "processed",
              "displayName": "processed",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "93cd019d-6936-4be4-b750-98fa7ff53541",
      "name": "Append to Activity Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -736,
        -912
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "id": "f35102bd-71c1-494d-a5b4-cc9289cf26d7",
      "name": "No New Data",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -736,
        -720
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 0 6 * * *"
            },
            {
              "field": "cronExpression",
              "expression": "0 0 18 * * *"
            }
          ]
        }
      },
      "id": "f74bc216-9533-4899-ac23-a34eabc17f3d",
      "name": "Post Schedule (0600 + 1800)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2000,
        336
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Config",
          "mode": "name"
        },
        "options": {}
      },
      "id": "65d2b83e-9fd6-4075-bc29-744f8eca861e",
      "name": "Read Config Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1520,
        336
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all();\nconst config = {};\nfor (const row of rows) {\n  const key = String(row.json.Key || row.json.key || '').trim();\n  const value = String(row.json.Value || row.json.value || '').trim();\n  if (key) config[key] = value;\n}\n\nreturn [{ json: config }];\n"
      },
      "id": "4ed4b2f7-af58-406b-9ab6-10ae272055b7",
      "name": "Parse Config",
      "type": "n8n-nodes-base.code",
      "position": [
        -1280,
        336
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.posting_active }}",
              "rightValue": "true"
            }
          ]
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "3e8949f5-c79e-40b0-a2a2-d1a91b163a73",
      "name": "Posting Active?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1040,
        336
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {},
      "id": "d6409c94-352b-461a-9ffa-8a14062a7b9d",
      "name": "Posting Disabled - Stop",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -800,
        528
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "jsCode": "// Determine which statuses to fetch based on approval_required config\nconst config = $('Parse Config').first().json;\nconst approvalRequired = (config.approval_required || 'false').toLowerCase() === 'true';\n\n// If approval required, only get 'approved' posts\n// If not, get both 'ready' and 'approved' posts\nreturn [{\n  json: {\n    ...config,\n    fetch_status: approvalRequired ? 'approved' : 'ready_or_approved',\n    approval_required: approvalRequired\n  }\n}];"
      },
      "id": "60175951-fddb-4b7f-8036-11cb3051cb3c",
      "name": "Check Approval Mode",
      "type": "n8n-nodes-base.code",
      "position": [
        -800,
        240
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config1').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Posts",
          "mode": "name"
        },
        "options": {}
      },
      "id": "5461462b-ee88-4402-bcec-b6c657c70b49",
      "name": "Get All Posts",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -560,
        240
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// V1: X/Twitter only \u2014 hardcoded platform\nconst platform = 'x';\n\nconst config = $('Check Approval Mode').first().json;\nconst approvalRequired = config.approval_required;\nconst allPosts = $input.all();\n\n// Filter by status\nconst validPosts = allPosts.filter(p => {\n  const status = (p.json.status || '').toLowerCase();\n  if (approvalRequired) {\n    return status === 'approved';\n  } else {\n    return status === 'ready' || status === 'approved';\n  }\n});\n\n// Find first unposted post for X\nconst unposted = validPosts.find(p => {\n  if (p.json.x_posted && p.json.x_posted !== '') return false;\n  if (!p.json.x_text || p.json.x_text.trim() === '') return false;\n  return true;\n});\n\nif (unposted) {\n  return [{\n    json: {\n      has_post: true,\n      platform: platform,\n      post_id: unposted.json.id,\n      post_text: unposted.json.x_text,\n      image_url: unposted.json.image_url || '',\n      base_text: unposted.json.base_text || '',\n      mood: unposted.json.mood || '',\n      x_account_id: config.x_account_id || '',\n      instagram_account_id: config.instagram_account_id || '',\n      tiktok_account_id: config.tiktok_account_id || '',\n      youtube_account_id: config.youtube_account_id || '',\n      blotato_key: config.blotato_key || ''\n    }\n  }];\n}\n\nreturn [{\n  json: {\n    has_post: false,\n    platform: platform,\n    reason: 'No unposted content available for X'\n  }\n}];"
      },
      "id": "319d4d0d-aabd-41ba-af8d-c0acbf9de65a",
      "name": "Find Post for Platform",
      "type": "n8n-nodes-base.code",
      "position": [
        -320,
        240
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_post }}"
            }
          ]
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "26fb5680-8068-43aa-8967-b6efb9952c78",
      "name": "Has Post?",
      "type": "n8n-nodes-base.if",
      "position": [
        -80,
        240
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {},
      "id": "21e9c16b-828c-4026-a994-e1789cea6e7c",
      "name": "No Post Available",
      "type": "n8n-nodes-base.noOp",
      "position": [
        160,
        432
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "jsCode": "// Build the Blotato API request body based on platform\nconst data = $input.first().json;\nconst platform = data.platform;\nconst postText = data.post_text;\nconst imageUrl = data.image_url;\n\nlet accountId, body;\n\nswitch (platform) {\n  case 'x':\n    accountId = data.x_account_id;\n    body = {\n      post: {\n        accountId: accountId,\n        content: {\n          text: postText,\n          mediaUrls: imageUrl ? [imageUrl] : [],\n          platform: 'twitter'\n        },\n        target: {\n          targetType: 'twitter'\n        }\n      }\n    };\n    break;\n\n  case 'instagram':\n    accountId = data.instagram_account_id;\n    body = {\n      post: {\n        accountId: accountId,\n        content: {\n          text: postText,\n          mediaUrls: imageUrl ? [imageUrl] : [],\n          platform: 'instagram'\n        },\n        target: {\n          targetType: 'instagram',\n          mediaType: 'post'\n        }\n      }\n    };\n    break;\n\n  case 'tiktok':\n    accountId = data.tiktok_account_id;\n    body = {\n      post: {\n        accountId: accountId,\n        content: {\n          text: postText,\n          mediaUrls: imageUrl ? [imageUrl] : [],\n          platform: 'tiktok'\n        },\n        target: {\n          targetType: 'tiktok',\n          privacyLevel: 'PUBLIC_TO_EVERYONE',\n          disabledComments: false,\n          disabledDuet: false,\n          disabledStitch: false,\n          isAiGenerated: false,\n          isBrandedContent: false,\n          isYourBrand: true\n        }\n      }\n    };\n    break;\n\n  case 'youtube':\n    accountId = data.youtube_account_id;\n    body = {\n      post: {\n        accountId: accountId,\n        content: {\n          text: postText,\n          mediaUrls: imageUrl ? [imageUrl] : [],\n          platform: 'youtube'\n        },\n        target: {\n          targetType: 'youtube'\n        }\n      }\n    };\n    break;\n\n  default:\n    throw new Error(`Unknown platform: ${platform}`);\n}\n\nreturn [{\n  json: {\n    platform: platform,\n    post_id: data.post_id,\n    blotato_key: data.blotato_key,\n    request_body: JSON.stringify(body)\n  }\n}];\n"
      },
      "id": "a2a907b3-deb9-4440-9c3e-eb12d1624fc7",
      "name": "Build Blotato Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        128
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://backend.blotato.com/v2/posts",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "blotato-api-key",
              "value": "blt_0V1OhRjLdeCcJWTD3I+ii0tf9otE5bCAd8ZRm2h2SHw="
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.request_body }}",
        "options": {}
      },
      "id": "ae18e8b3-94bc-4293-ab06-6f2105daea52",
      "name": "Post to Blotato",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        128
      ],
      "typeVersion": 4.2
    },
    {
      "parameters": {
        "jsCode": "// Prepare the update to mark this post as posted on the relevant platform\nconst buildPayload = $('Build Blotato Payload').first().json;\nconst postResult = $input.first().json;\nconst platform = buildPayload.platform;\nconst postId = buildPayload.post_id;\nconst now = new Date().toISOString();\n\n// Build update object - only set the relevant platform's posted timestamp\nconst update = {\n  id: postId\n};\n\nupdate[platform + '_posted'] = now;\n\n// If all 4 platforms have been posted, update status to 'posted'\n// We'll check this in the sheet update step\n\nreturn [{\n  json: {\n    ...update,\n    platform: platform,\n    blotato_post_id: postResult.postSubmissionId || postResult.id || ''\n  }\n}];"
      },
      "id": "5d9786cf-ecc6-4ad0-9978-9f98bdb89876",
      "name": "Prepare Posted Update",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        128
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config1').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Posts",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "id"
          ],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "base_text",
              "displayName": "base_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "mood",
              "displayName": "mood",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "category",
              "displayName": "category",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "x_text",
              "displayName": "x_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "instagram_text",
              "displayName": "instagram_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "tiktok_text",
              "displayName": "tiktok_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "youtube_text",
              "displayName": "youtube_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "image_url",
              "displayName": "image_url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "x_posted",
              "displayName": "x_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "instagram_posted",
              "displayName": "instagram_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "tiktok_posted",
              "displayName": "tiktok_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "youtube_posted",
              "displayName": "youtube_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "source_activity_id",
              "displayName": "source_activity_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "3d15b4a3-402b-4803-98d6-21ccf71ae1b6",
      "name": "Mark Posted in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        880,
        128
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Config1').first().json.sheet_id }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Log",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {
            "timestamp": "={{ new Date().toISOString() }}",
            "action": "posted",
            "platform": "={{ $('Prepare Posted Update').first().json.platform }}",
            "post_id": "={{ $('Prepare Posted Update').first().json.id }}",
            "details": "=Posted to {{ $('Prepare Posted Update').first().json.platform }} via Blotato. Submission ID: {{ $('Prepare Posted Update').first().json.blotato_post_id }}"
          },
          "matchingColumns": [
            "id"
          ],
          "schema": [
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "action",
              "displayName": "action",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "platform",
              "displayName": "platform",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "post_id",
              "displayName": "post_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "details",
              "displayName": "details",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "x_posted",
              "displayName": "x_posted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "blotato_post_id",
              "displayName": "blotato_post_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "f22d5990-6687-42b0-b332-51b99295f9e6",
      "name": "Log Posting",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1120,
        128
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "sheet_id",
              "type": "string",
              "value": "1drU2R2jgijYQ5yEWPNWOnveA900WnT2BvNa4Wwn27Hs"
            }
          ]
        },
        "options": {}
      },
      "id": "de6203d0-7c7e-4aa5-a849-7d56380729de",
      "name": "Config1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1760,
        336
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {},
      "id": "d3304d21-4bb0-4e0d-90a6-7092a1de92c9",
      "name": "Post Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -2000,
        528
      ],
      "typeVersion": 1
    }
  ],
  "connections": {
    "Content Schedule (0530 + 1730)": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Get Unprocessed Activities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Unprocessed Activities": {
      "main": [
        [
          {
            "node": "Filter Unprocessed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Unprocessed": {
      "main": [
        [
          {
            "node": "Process One Activity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process One Activity": {
      "main": [
        [
          {
            "node": "All Activities Done",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "OpenAI Generate Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Generate Content": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Write to Posts Tab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write to Posts Tab": {
      "main": [
        [
          {
            "node": "Mark Activity Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Activity Processed": {
      "main": [
        [
          {
            "node": "Log Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Generation": {
      "main": [
        [
          {
            "node": "Process One Activity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CSV Sync Schedule (0515 + 1715)": {
      "main": [
        [
          {
            "node": "Read CSV from Drive",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Sheet Activities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read CSV from Drive": {
      "main": [
        [
          {
            "node": "Parse CSV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CSV": {
      "main": [
        [
          {
            "node": "Find New Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Sheet Activities": {
      "main": [
        [
          {
            "node": "Find New Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find New Rows": {
      "main": [
        [
          {
            "node": "Has New Rows?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has New Rows?": {
      "main": [
        [
          {
            "node": "Append to Activity Tab",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No New Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Schedule (0600 + 1800)": {
      "main": [
        [
          {
            "node": "Config1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Config Tab": {
      "main": [
        [
          {
            "node": "Parse Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Config": {
      "main": [
        [
          {
            "node": "Posting Active?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Posting Active?": {
      "main": [
        [
          {
            "node": "Check Approval Mode",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Posting Disabled - Stop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Approval Mode": {
      "main": [
        [
          {
            "node": "Get All Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Posts": {
      "main": [
        [
          {
            "node": "Find Post for Platform",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Post for Platform": {
      "main": [
        [
          {
            "node": "Has Post?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Post?": {
      "main": [
        [
          {
            "node": "Build Blotato Payload",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Post Available",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Blotato Payload": {
      "main": [
        [
          {
            "node": "Post to Blotato",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Blotato": {
      "main": [
        [
          {
            "node": "Prepare Posted Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Posted Update": {
      "main": [
        [
          {
            "node": "Mark Posted in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Posted in Sheet": {
      "main": [
        [
          {
            "node": "Log Posting",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config1": {
      "main": [
        [
          {
            "node": "Read Config Tab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Manual Trigger": {
      "main": [
        [
          {
            "node": "Config1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Europe/London"
  },
  "versionId": "lefbot-v1-master-9feb-2026",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "jyjdbi0wr13gX2s4",
  "tags": [
    {
      "name": "LefBot Social",
      "id": "7YPpwa8plEf5ZWSW",
      "updatedAt": "2026-02-08T19:16:33.470Z",
      "createdAt": "2026-02-08T19:16:33.470Z"
    }
  ]
}