{
  "nodes": [
    {
      "id": "d4e41eb2-473a-4496-a54f-7893da5185ae",
      "name": "When Telegram Callback Triggered",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        128,
        4064
      ],
      "parameters": {
        "updates": [
          "callback_query"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "62e8e538-4ad3-4cb1-91be-df8983e9e4b1",
      "name": "Extract Telegram Callback",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        4064
      ],
      "parameters": {
        "jsCode": "const cb = $json.callback_query;\nif (!cb) throw new Error('No callback_query');\n\nconst data = cb.data || '';\n\n// EXPECT: approve::rowId OR reject::rowId\nconst [action, rowId] = data.split('::');\n\nreturn [{\n  json: {\n    action: action,         // approve / reject\n    rowId: rowId,           // actual row id\n    chatId: String(cb.message.chat.id),\n    messageId: cb.message.message_id,\n    callbackQueryId: cb.id\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "e3eac23d-756f-4a6e-abd7-ae3f50887c04",
      "name": "Update Approval in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        800,
        3968
      ],
      "parameters": {
        "columns": {
          "value": {
            "Row ID": "={{ $json.rowId }}",
            "Status": "Approved",
            "row_number": 0
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Topic",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Topic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Style",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tweet",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Tweet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hashtags",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Scheduled Time",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Scheduled Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Row ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Row ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Row ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "YOUR_GOOGLE_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4901d347-fa14-4593-a365-80464c8e5825",
      "name": "Update Rejection in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        800,
        4160
      ],
      "parameters": {
        "columns": {
          "value": {
            "Row ID": "={{ $('Extract Telegram Callback').item.json.rowId }}",
            "Status": "Rejected",
            "row_number": 0
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Topic",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Topic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Style",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tweet",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Tweet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hashtags",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Scheduled Time",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Scheduled Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Row ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Row ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Row ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "YOUR_GOOGLE_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "edd53ab8-1bd8-403a-810e-e7104f1c8612",
      "name": "Send Approval Confirmation",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1024,
        3968
      ],
      "parameters": {
        "text": "=\u2705 Tweet approved! Status updated in sheet.",
        "chatId": "={{ $('Extract Telegram Callback').first().json.chatId }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7e55529d-cccf-4a3a-ad4f-f111c0dea1bb",
      "name": "Send Rejection Confirmation",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1024,
        4160
      ],
      "parameters": {
        "text": "=\u274c Tweet rejected. Status updated in sheet.",
        "chatId": "={{ $('Extract Telegram Callback').first().json.chatId }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "884e576f-bf5c-4360-b44f-ef834318e50f",
      "name": "When Scheduled at 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        128,
        3488
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "f1a2eece-cf08-4e43-84a8-873e295ab488",
      "name": "Process Article Extraction",
      "type": "n8n-nodes-base.code",
      "position": [
        784,
        3488
      ],
      "parameters": {
        "jsCode": "if ($input.all().length > 1 && $input.itemIndex > 0) {\n  return [];\n}\n// SAFE RSS ACCESS\nconst rssItems = $('Fetch Google News RSS').all();\n\nif (!rssItems.length) {\n  throw new Error('RSS data missing');\n}\n\nconst xml = rssItems[0].json.data;\n\n\n// GET USED ARTICLES SAFELY (WORKS EVEN IF SHEET EMPTY)\nlet usedTitles = [];\n\ntry {\n  const usedItems = $('Read Used Articles').all();\n\n  usedTitles = usedItems\n    .map(item => item.json[\"Title\"] || item.json[\"Title \"] || \"\")\n    .filter(Boolean)\n    .map(t => t.toLowerCase().trim());\n\n} catch (e) {\n  usedTitles = [];\n}\n\n\n// EXTRACT RSS ITEMS\nconst itemRegex = /<item>([\\s\\S]*?)<\\/item>/g;\nconst items = [];\nlet match;\n\nwhile ((match = itemRegex.exec(xml)) !== null) {\n  items.push(match[1]);\n}\n\n\n// PARSE ARTICLES\nconst articles = items.slice(0, 10).map(item => {\n  const title = (item.match(/<title>([\\s\\S]*?)<\\/title>/) || [])[1] || '';\n  const description = (item.match(/<description>([\\s\\S]*?)<\\/description>/) || [])[1] || '';\n  const pubDate = (item.match(/<pubDate>([\\s\\S]*?)<\\/pubDate>/) || [])[1] || '';\n  const link = (item.match(/<link>([\\s\\S]*?)<\\/link>/) || [])[1] || '';\n\n  return {\n    title: title\n      .replace(/<!\\[CDATA\\[|\\]\\]>/g, '')\n      .replace(/\\s*-\\s*[\\w.]+\\.(com|in|org|net|co\\.in)$/i, '')\n      .trim(),\n\n    description: description\n      .replace(/<!\\[CDATA\\[|\\]\\]>/g, '')\n      .replace(/<[^>]+>/g, '')\n      .trim(),\n\n    pubDate: pubDate.trim(),\n    link: link.trim()\n  };\n});\n\n\n// REMOVE EMPTY TITLES\nconst validArticles = articles.filter(a => a.title.length > 5);\n\n\n// REMOVE ALREADY USED ARTICLES\nconst unused = validArticles.filter(a =>\n  !usedTitles.includes(a.title.toLowerCase().trim())\n);\n\n\n// HANDLE FULLY USED CASE\nconst pool = unused.length > 0 ? unused : validArticles;\n\nif (pool.length === 0) {\n  throw new Error('No valid articles found in RSS feed');\n}\n\n\n// RANDOM PICK\nconst randomIndex = Math.floor(Math.random() * pool.length);\nconst top = pool[randomIndex];\n\n\n// EXTRA SAFETY CHECK\nconst isAlreadyUsed = usedTitles.includes(top.title.toLowerCase().trim());\n\nif (isAlreadyUsed && unused.length > 0) {\n  const fallback = unused[0];\n\n  return [{\n    json: {\n      topic: fallback.title,\n      description: fallback.description,\n      pubDate: fallback.pubDate,\n      link: fallback.link,\n      unusedCount: unused.length,\n      totalArticles: validArticles.length,\n      usedCount: usedTitles.length,\n      note: 'fallback used'\n    }\n  }];\n}\n\n\n// FINAL OUTPUT\nreturn [{\n  json: {\n    topic: top.title,\n    description: top.description,\n    pubDate: top.pubDate,\n    link: top.link,\n    unusedCount: unused.length,\n    totalArticles: validArticles.length,\n    usedCount: usedTitles.length,\n    note: 'random pick'\n  }\n}];"
      },
      "executeOnce": false,
      "typeVersion": 2
    },
    {
      "id": "f24c22d5-98c0-4c1a-a3f9-abfdd3a02e5d",
      "name": "AI Tweet Generation Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1024,
        3456
      ],
      "parameters": {
        "text": "=You are a professional Twitter/X content strategist.\n\nBased on this trending news article:\nTopic: {{ $json.topic }}\nSummary: {{ $json.description }}\nPublished: {{ $json.pubDate }}\n\nGenerate exactly 5 tweets, each in a different style:\n1. Informational\n2. Hot take\n3. Question\n4. Quote style (NO quotation marks)\n5. Thread hook (ends with 1/5)\n\nSTRICT RULES:\n- Return ONLY valid JSON\n- DO NOT include markdown, backticks, explanation, or extra text\n- Use ONLY standard double quotes (\")\n- Escape any quotes inside text\n- Max 260 characters per tweet\n- Max 3 hashtags per tweet\n\nFORMAT:\n{\n  \"tweets\": [\n    {\"style\": \"informational\", \"text\": \"...\", \"hashtags\": \"...\"},\n    {\"style\": \"hot take\", \"text\": \"...\", \"hashtags\": \"...\"},\n    {\"style\": \"question\", \"text\": \"...\", \"hashtags\": \"...\"},\n    {\"style\": \"quote\", \"text\": \"...\", \"hashtags\": \"...\"},\n    {\"style\": \"thread hook\", \"text\": \"...\", \"hashtags\": \"...\"}\n  ]\n}",
        "options": {
          "systemMessage": "You are a professional Twitter/X content strategist. Always return raw JSON only. Never add markdown, backticks, or explanation outside the JSON object."
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "c2db3023-fb97-4b87-a6fb-3984236f3df2",
      "name": "Parse Tweet Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1408,
        3488
      ],
      "parameters": {
        "jsCode": "const raw = $json.output || $json.text || JSON.stringify($json);\n\n// Nuclear clean all problematic characters\nlet clean = raw\n  .replace(/[\\u201C\\u201D\\u201E\\u201F\\u2033\\u2036]/g, '\"')\n  .replace(/[\\u2018\\u2019\\u201A\\u201B\\u2032\\u2035]/g, \"'\")\n  .replace(/\\\\n/g, ' ')\n  .replace(/\\\\t/g, ' ')\n  .replace(/\\\\r/g, ' ')\n  .replace(/```json/g, '')\n  .replace(/```/g, '')\n  .trim();\n\n// Extract JSON object\nconst match = clean.match(/\\{[\\s\\S]*\\}/);\nif (!match) {\n  return [{\n    json: {\n      error: 'AI output invalid',\n      rawOutput: raw\n    }\n  }];\n}\n\n// Fix any remaining smart quotes inside JSON strings\nclean = clean.replace(/\"\\s*:\\s*\"([^\"]*?)\"/g, (m, p1) => {\n  return m.replace(/[\\u201C\\u201D\\u201E\\u201F]/g, '\\\\\"');\n});\n\nlet parsed;\ntry {\n  parsed = JSON.parse(clean);\n} catch(e) {\n  // Last resort \u2014 manually extract tweets using regex\n  const tweetRegex = /\"text\"\\s*:\\s*\"(.*?)(?<!\\\\)\"/g;\n  const hashRegex = /\"hashtags\"\\s*:\\s*\"(.*?)(?<!\\\\)\"/g;\n  const styleRegex = /\"style\"\\s*:\\s*\"(.*?)(?<!\\\\)\"/g;\n\n  const texts = [...clean.matchAll(/\"text\"\\s*:\\s*\"([^\"]+)\"/g)].map(m => m[1]);\n  const hashes = [...clean.matchAll(/\"hashtags\"\\s*:\\s*\"([^\"]+)\"/g)].map(m => m[1]);\n  const styles = [...clean.matchAll(/\"style\"\\s*:\\s*\"([^\"]+)\"/g)].map(m => m[1]);\n\n  if (texts.length === 0) throw new Error('Could not parse tweets: ' + e.message);\n\n  parsed = {\n    tweets: texts.map((text, i) => ({\n      style: styles[i] || 'unknown',\n      text: text,\n      hashtags: hashes[i] || ''\n    }))\n  };\n}\n\nconst topic = $('Process Article Extraction').first().json.topic;\nconst description = $('Process Article Extraction').first().json.description;\n\nconst times = ['08:00 IST', '10:00 IST', '12:00 IST', '14:00 IST', '16:00 IST'];\n\nconst now = new Date();\nconst istOffset = 5.5 * 60 * 60000;\nconst istNow = new Date(now.getTime() + istOffset);\nconst today = istNow.toISOString().split('T')[0];\nconst createdAtIST = istNow.toISOString().replace('Z', '+05:30');\n\nconst rows = parsed.tweets.map((tweet, index) => {\n  const fullPost = tweet.text + ' ' + tweet.hashtags;\n  const rowId = tweet.style + '_' + today + '_' + Date.now();\n\n  const blocks = [\n    {\n      type: \"section\",\n      text: {\n        type: \"mrkdwn\",\n        text: \"*Tweet ready for approval*\\n\\n*Style:* \" + tweet.style + \"\\n*Scheduled:* \" + times[index] + \"\\n*Topic:* \" + topic + \"\\n\\n*Tweet:*\\n\" + tweet.text + \"\\n\\n\" + tweet.hashtags\n      }\n    },\n    {\n      type: \"actions\",\n      block_id: rowId,\n      elements: [\n        {\n          type: \"button\",\n          text: { type: \"plain_text\", text: \"Approve\" },\n          style: \"primary\",\n          value: \"approved\",\n          action_id: \"approve_tweet\"\n        },\n        {\n          type: \"button\",\n          text: { type: \"plain_text\", text: \"Reject\" },\n          style: \"danger\",\n          value: \"rejected\",\n          action_id: \"reject_tweet\"\n        }\n      ]\n    }\n  ];\n\n  return {\n    json: {\n      topic: topic,\n      description: description,\n      style: tweet.style,\n      tweet: tweet.text,\n      hashtags: tweet.hashtags,\n      fullPost: fullPost,\n      charCount: fullPost.length,\n      scheduledTime: times[index],\n      scheduledDate: today,\n      status: 'Pending',\n      createdAt: createdAtIST,\n      rowId: rowId,\n      slackBlocks: JSON.stringify(blocks)\n    }\n  };\n});\n\nreturn rows;"
      },
      "typeVersion": 2
    },
    {
      "id": "a2fd206b-ce48-456a-a44b-3b8f0f785019",
      "name": "Log Tweets in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1856,
        3488
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.toFormat('yyyy-MM-dd') }}",
            "Style": "={{ $('Parse Tweet Data').item.json.style }}",
            "Topic": "={{ $('Parse Tweet Data').item.json.topic }}",
            "Tweet": "={{ $('Parse Tweet Data').item.json.tweet }}",
            "Row ID": "={{ $('Parse Tweet Data').item.json.rowId }}",
            "Status": "={{ $('Parse Tweet Data').item.json.status }}",
            "Hashtags": "={{ $('Parse Tweet Data').item.json.hashtags }}",
            "Created At": "={{ $('Parse Tweet Data').item.json.createdAt }}",
            "Scheduled Time": "={{ $('Parse Tweet Data').item.json.scheduledTime }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Topic",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Topic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Style",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tweet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Tweet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Scheduled Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Scheduled Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Row ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Row ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "YOUR_GOOGLE_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6c823455-2add-4c77-a553-0efc5ae4d33b",
      "name": "Fetch Google News RSS",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        352,
        3488
      ],
      "parameters": {
        "url": "https://news.google.com/rss?hl=en-IN&gl=IN&ceid=IN:en",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "n8n-bot"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "4dd7cf28-6f51-488a-9f11-f04fcd1c0c1e",
      "name": "Read Used Articles",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        576,
        3488
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "YOUR_GOOGLE_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4510619f-40c4-4bc9-a343-fb34a5dba556",
      "name": "Log Article as Used",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2304,
        3488
      ],
      "parameters": {
        "columns": {
          "value": {
            "Title ": "={{ $('Process Article Extraction').first().json.topic }}",
            "Date Used": "={{ $now.toFormat('yyyy-MM-dd') }}"
          },
          "schema": [
            {
              "id": "Date Used",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date Used",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title ",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Title ",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Row ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "YOUR_GOOGLE_NAME"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c0a0ce95-ba4c-49d0-ade7-273a6d19f1c9",
      "name": "Limit to 10 Entries",
      "type": "n8n-nodes-base.limit",
      "position": [
        2080,
        3488
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "68ad6247-7deb-48be-b7d9-ee72979cf01d",
      "name": "Send Telegram Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1632,
        3488
      ],
      "parameters": {
        "text": "=\ud83d\udcf0 *{{ $json.topic }}*\n\n\u270d\ufe0f {{ $json.tweet }}\n\n{{ $json.hashtags }}\n\n\u23f0 {{ $json.scheduledTime }}\n\ud83c\udd94 `{{ $json.rowId }}`",
        "chatId": "=REPLACE_WITH_YOUR_CHAT_ID",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\u2705 Approve",
                    "additionalFields": {
                      "callback_data": "={{ 'approve::' + $json.rowId }}"
                    }
                  },
                  {
                    "text": "\u274c Reject",
                    "additionalFields": {
                      "callback_data": "={{ 'reject::' + $json.rowId }}"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "45b79175-4283-402d-9ea4-9b2b04e0af95",
      "name": "AI Groq Interaction",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        1024,
        3648
      ],
      "parameters": {
        "model": "qwen/qwen3-32b",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "62a7666a-2067-4f12-8011-22e418ac7708",
      "name": "Decision: Approve or Reject",
      "type": "n8n-nodes-base.if",
      "position": [
        576,
        4064
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.action }}",
              "value2": "approve",
              "operation": "contains"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9d3e0133-78ec-497c-8cf1-2fcfedf3baab",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        3360
      ],
      "parameters": {
        "width": 464,
        "height": 704,
        "content": "## AI Tweet Generation & Approval Workflow\n\n### How it works\n\n1. Triggers on a schedule to fetch Google News RSS feed.\n2. Retrieves and filters already used articles from Google Sheets.\n3. Generates tweets based on news articles and schedules them.\n4. Sends the generated tweets as text messages and logs them.\n5. Updates Google Sheets with tweet logs and marks articles as used.\n6. Processes Telegram callbacks for approval and updates Google Sheets based on decisions.\n\n### Setup steps\n\n- [ ] Configure Google Sheets API credentials for logging and data retrieval.\n- [ ] Set up Telegram bot credentials for sending messages.\n- [ ] Configure the schedule for fetching news RSS feeds.\n\n### Customization\n\nAdjust the criteria for tweet generation and approval logic as needed."
      },
      "typeVersion": 1
    },
    {
      "id": "2561d66a-6fcf-46fd-b724-7b8e1a9f3976",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        3856
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 512,
        "content": "## Telegram callback handling\n\nTriggers on Telegram callback, processes data, and updates Google Sheets based on approval or rejection."
      },
      "typeVersion": 1
    },
    {
      "id": "8dfa7650-aee9-4e69-bf11-a9bc0585477c",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        3360
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 304,
        "content": "## News fetching and processing\n\nSchedules and triggers the fetching of news RSS, retrieves used articles, extracts useful data, and prepares it for tweet generation."
      },
      "typeVersion": 1
    },
    {
      "id": "5294b3b8-2679-471b-9e5e-472a1e7d09cd",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        3328
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 480,
        "content": "## Tweet generation\n\nGenerates tweets using AI based on processed articles."
      },
      "typeVersion": 1
    },
    {
      "id": "d9cc4679-27a3-488a-ac3c-5431983d0fd5",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        3360
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Tweet scheduling and sending\n\nSchedules the generated tweets, sends them as telegram messages, and logs the details."
      },
      "typeVersion": 1
    },
    {
      "id": "fb030892-fe05-4258-b2a3-4f26bbcd2857",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        3360
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## Article logging and finalization\n\nLogs tweets to Google Sheets, limits the number of logs, and marks articles as used."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Parse Tweet Data": {
      "main": [
        [
          {
            "node": "Send Telegram Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Used Articles": {
      "main": [
        [
          {
            "node": "Process Article Extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Groq Interaction": {
      "ai_languageModel": [
        [
          {
            "node": "AI Tweet Generation Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Limit to 10 Entries": {
      "main": [
        [
          {
            "node": "Log Article as Used",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Tweets in Sheets": {
      "main": [
        [
          {
            "node": "Limit to 10 Entries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Google News RSS": {
      "main": [
        [
          {
            "node": "Read Used Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Telegram Message": {
      "main": [
        [
          {
            "node": "Log Tweets in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Scheduled at 8 AM": {
      "main": [
        [
          {
            "node": "Fetch Google News RSS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Tweet Generation Agent": {
      "main": [
        [
          {
            "node": "Parse Tweet Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Telegram Callback": {
      "main": [
        [
          {
            "node": "Decision: Approve or Reject",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Approval in Sheets": {
      "main": [
        [
          {
            "node": "Send Approval Confirmation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Article Extraction": {
      "main": [
        [
          {
            "node": "AI Tweet Generation Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Rejection in Sheets": {
      "main": [
        [
          {
            "node": "Send Rejection Confirmation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decision: Approve or Reject": {
      "main": [
        [
          {
            "node": "Update Approval in Sheets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Rejection in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Telegram Callback Triggered": {
      "main": [
        [
          {
            "node": "Extract Telegram Callback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}