{
  "name": "MADIAD Hub - Đăng theo lịch từ Google Sheets",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -160,
        0
      ],
      "id": "Chạy mỗi 15 phút",
      "name": "Chạy mỗi 15 phút"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Cấu Hình",
          "mode": "name"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        60,
        0
      ],
      "id": "Đọc Cấu Hình",
      "name": "Đọc Cấu Hình"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Lịch Đăng",
          "mode": "name"
        },
        "options": {
          "dataLocationOnSheet": {
            "values": {
              "rangeDefinition": "specifyRange",
              "headerRow": 1,
              "firstDataRow": 3
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        280,
        0
      ],
      "id": "Đọc Lịch Đăng",
      "name": "Đọc Lịch Đăng"
    },
    {
      "parameters": {
        "jsCode": "\nconst cfg = {};\nfor (const it of $('Đọc Cấu Hình').all()) {\n  const k = String(it.json['Khoá'] ?? '').trim();\n  if (k) cfg[k] = String(it.json['Giá Trị'] ?? '').trim();\n}\nif (!cfg.profile_id) throw new Error('Chưa điền profile_id ở tab Cấu Hình. Lấy trong Dashboard MADIAD Hub.');\nconst KENH = { 'Facebook': 'facebook', 'Instagram': 'instagram', 'TikTok': 'tiktok', 'YouTube': 'youtube', 'LinkedIn': 'linkedin', 'X': 'x', 'Threads': 'threads', 'Pinterest': 'pinterest', 'Reddit': 'reddit', 'Bluesky': 'bluesky', 'Google Business': 'google_business', 'Telegram': 'telegram', 'Discord': 'discord', 'Slack': 'slack', 'Mastodon': 'mastodon', 'Nostr': 'nostr', 'Lemmy': 'lemmy', 'dev.to': 'devto', 'Hashnode': 'hashnode', 'WordPress': 'wordpress', 'Whop': 'whop', 'listmonk': 'listmonk' };\nconst IMG = ['.jpg', '.jpeg', '.png', '.webp', '.gif'];\nconst VID = ['.mp4', '.mov', '.m4v', '.webm'];\nconst NEEDS_TITLE = ['youtube', 'reddit', 'pinterest'];\nconst OPEN = ['', 'Nháp', 'Chờ Đăng'];\n\nfunction buildPost(r, cfg, KENH, IMG, VID, NEEDS_TITLE, execId) {\n  const caption = String(r['Nội Dung'] ?? '').trim();\n  const raw = String(r['Kênh Đăng'] ?? '').trim();\n  // Dòng chưa soạn xong thì bỏ qua im lặng, không coi là lỗi.\n  if (!caption || !raw) return null;\n\n  const platforms = raw.split(',').map(s => KENH[s.trim()]).filter(Boolean);\n  if (!platforms.length) return { _row: r.row_number, _err: 'Cột Kênh Đăng không khớp kênh nào. Chọn lại từ dropdown.' };\n\n  const links = String(r['Media'] ?? '').split(/[\\n,]/).map(s => s.trim()).filter(Boolean);\n  const bad = links.filter(l => !l.startsWith('https://'));\n  if (bad.length) return { _row: r.row_number, _err: 'Link media phải bắt đầu bằng https:// , đang là: ' + bad[0] };\n\n  const low = links.map(l => l.toLowerCase());\n  let endpoint = 'text';\n  if (links.length) endpoint = low.some(l => VID.some(e => l.includes(e))) ? 'video' : 'photos';\n\n  const body = { profile_id: cfg.profile_id, platforms, caption };\n  const title = String(r['Tiêu Đề'] ?? '').trim();\n  if (title) for (const p of platforms) if (NEEDS_TITLE.includes(p)) body[p + '_title'] = title;\n  const fc = String(r['Bình Luận Đầu'] ?? '').trim();\n  if (fc) body.first_comment = fc;\n  if (platforms.includes('pinterest')) {\n    if (!cfg.pinterest_board_id) return { _row: r.row_number, _err: 'Đăng Pinterest cần pinterest_board_id ở tab Cấu Hình.' };\n    body.pinterest_board_id = cfg.pinterest_board_id;\n  }\n  if (endpoint === 'video') body.video_url = links[0];\n  if (endpoint === 'photos') body.photo_urls = links;\n\n  // Mã Bài là chìa khoá chống đăng trùng. Sinh TRƯỚC khi gọi API và ghi ngay xuống sheet, để một lần chạy lại không bao giờ tạo chìa khác cho cùng một bài.\n  const id = String(r['Mã Bài'] ?? '').trim() || ('P-' + execId + '-' + r.row_number);\n  return { _row: r.row_number, _id: id, _endpoint: endpoint, body };\n}\n\nconst perRun = parseInt(cfg.rows_per_run || '10', 10) || 10;\nconst out = [];\nfor (const it of $input.all()) {\n  if (out.length >= perRun) break;\n  const r = it.json;\n  if (String(r['Hành Động'] ?? '').trim() !== 'Đăng ngay') continue;\n  if (!OPEN.includes(String(r['Trạng Thái'] ?? '').trim())) continue;\n  const b = buildPost(r, cfg, KENH, IMG, VID, NEEDS_TITLE, $execution.id);\n  if (b) out.push({ json: b });\n}\nreturn out;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        500,
        0
      ],
      "id": "Chuẩn bị bài đăng",
      "name": "Chuẩn bị bài đăng"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "version": 2
          },
          "conditions": [
            {
              "id": "c",
              "operator": {
                "type": "string",
                "operation": "notExists"
              },
              "leftValue": "={{ $json._err }}",
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        720,
        0
      ],
      "id": "Dòng hợp lệ?",
      "name": "Dòng hợp lệ?"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Lịch Đăng",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "row_number": "={{ $json._row }}",
            "Mã Bài": "={{ $json._id }}",
            "Trạng Thái": "Đang Đăng",
            "Lỗi": ""
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": []
        },
        "options": {
          "dataLocationOnSheet": {
            "values": {
              "rangeDefinition": "specifyRange",
              "headerRow": 1,
              "firstDataRow": 3
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        940,
        -120
      ],
      "id": "Đánh dấu Đang Đăng",
      "name": "Đánh dấu Đang Đăng",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.madiad.com/v1/posts/{{ $('Chuẩn bị bài đăng').item.json._endpoint }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Madiad-Source",
              "value": "tpl-schedule-cron-v1"
            },
            {
              "name": "Idempotency-Key",
              "value": "={{ $('Chuẩn bị bài đăng').item.json._id }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "fullResponse": true
            }
          }
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($('Chuẩn bị bài đăng').item.json.body) }}"
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1160,
        -120
      ],
      "id": "Gọi MADIAD Hub",
      "name": "Gọi MADIAD Hub",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "version": 2
          },
          "conditions": [
            {
              "id": "c",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.statusCode }}",
              "rightValue": 300
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1380,
        -120
      ],
      "id": "Đăng được không?",
      "name": "Đăng được không?"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Lịch Đăng",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "row_number": "={{ $('Chuẩn bị bài đăng').item.json._row }}",
            "Trạng Thái": "={{ $json.body?.request_id ? 'Đang Đăng' : 'Đã Đăng' }}",
            "Mã Yêu Cầu": "={{ $json.body?.request_id ?? '' }}",
            "Link Bài Đăng": "={{ Object.entries($json.body?.results ?? {}).map(([k,v]) => k + ': ' + (v?.url ?? (v?.success === false ? ('hỏng, ' + (v?.error ?? '')) : 'đã nhận'))).join('\\n') }}",
            "Mã Bài Đăng": "={{ Object.entries($json.body?.results ?? {}).filter(([,v]) => v?.publish_id).map(([k,v]) => k + '=' + v.publish_id).join(';') }}",
            "Đăng Lúc": "={{ $json.body?.request_id ? '' : $now.setZone('Asia/Ho_Chi_Minh').toFormat('dd/MM/yyyy HH:mm') }}",
            "Lỗi": ""
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": []
        },
        "options": {
          "dataLocationOnSheet": {
            "values": {
              "rangeDefinition": "specifyRange",
              "headerRow": 1,
              "firstDataRow": 3
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1600,
        -220
      ],
      "id": "Ghi Kết Quả",
      "name": "Ghi Kết Quả",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Lịch Đăng",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "row_number": "={{ $('Chuẩn bị bài đăng').item.json._row }}",
            "Trạng Thái": "Lỗi",
            "Lỗi": "={{ $json.body?.error?.message ?? ('HTTP ' + ($json.statusCode ?? '?')) }}"
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": []
        },
        "options": {
          "dataLocationOnSheet": {
            "values": {
              "rangeDefinition": "specifyRange",
              "headerRow": 1,
              "firstDataRow": 3
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1600,
        -20
      ],
      "id": "Ghi Lỗi",
      "name": "Ghi Lỗi",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "[MADIAD Template] Lịch Đăng Hub"
        },
        "sheetName": {
          "__rl": true,
          "value": "Lịch Đăng",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "row_number": "={{ $json._row }}",
            "Trạng Thái": "Lỗi",
            "Lỗi": "={{ $json._err }}"
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": []
        },
        "options": {
          "dataLocationOnSheet": {
            "values": {
              "rangeDefinition": "specifyRange",
              "headerRow": 1,
              "firstDataRow": 3
            }
          }
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        940,
        140
      ],
      "id": "Ghi Lỗi Dữ Liệu",
      "name": "Ghi Lỗi Dữ Liệu",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "content": "## MADIAD Hub - Đăng theo lịch từ Google Sheets\n\n**Ba việc trước khi bật:**\n1. Chọn Google Sheet của bạn ở TẤT CẢ các node Google Sheets.\n2. Tạo credential **Header Auth** tên `MADIAD Hub`:\n   Name `Authorization` , Value `Bearer <API key của bạn>`\n   rồi chọn nó ở node gọi API.\n3. Điền `profile_id` ở tab Cấu Hình của sheet.\n\nMã Bài là chìa khoá chống đăng trùng. Sinh TRƯỚC khi gọi API và ghi ngay xuống sheet, để một lần chạy lại không bao giờ tạo chìa khác cho cùng một bài.\n\nhttps://hub.madiad.com/mau",
        "height": 470,
        "width": 440,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -620,
        -200
      ],
      "id": "Hướng dẫn",
      "name": "Hướng dẫn"
    }
  ],
  "connections": {
    "Chạy mỗi 15 phút": {
      "main": [
        [
          {
            "node": "Đọc Cấu Hình",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đọc Cấu Hình": {
      "main": [
        [
          {
            "node": "Đọc Lịch Đăng",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đọc Lịch Đăng": {
      "main": [
        [
          {
            "node": "Chuẩn bị bài đăng",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chuẩn bị bài đăng": {
      "main": [
        [
          {
            "node": "Dòng hợp lệ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dòng hợp lệ?": {
      "main": [
        [
          {
            "node": "Đánh dấu Đang Đăng",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ghi Lỗi Dữ Liệu",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đánh dấu Đang Đăng": {
      "main": [
        [
          {
            "node": "Gọi MADIAD Hub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gọi MADIAD Hub": {
      "main": [
        [
          {
            "node": "Đăng được không?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đăng được không?": {
      "main": [
        [
          {
            "node": "Ghi Kết Quả",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ghi Lỗi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  }
}