{
  "name": "MADIAD Hub - Bấm là chạy (webhook)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "madiad-hub-publish-now",
        "responseMode": "onReceived",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -160,
        0
      ],
      "id": "Nhận lệnh từ Sheet",
      "name": "Nhận lệnh từ Sheet",
      "webhookId": "madiad-hub-publish-now"
    },
    {
      "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 req = $('Nhận lệnh từ Sheet').first().json;\n// Nút \"Kiểm tra kết nối\" trong Apps Script gửi {ping:true}. Trả về rỗng, không đăng gì, không\n// ném lỗi: mục đích của nó chỉ là chứng minh địa chỉ webhook sống và luồng đang bật.\nif (req?.body?.ping === true || req?.query?.ping === 'true') return [];\nconst want = Number(req?.body?.row ?? req?.query?.row ?? 0);\nif (!want) throw new Error('Lệnh gửi lên thiếu số dòng. Kiểm tra lại Apps Script.');\nfor (const it of $input.all()) {\n  const r = it.json;\n  if (Number(r.row_number) !== want) continue;\n  if (String(r['Hành Động'] ?? '').trim() !== 'Đăng ngay') return [];\n  const b = buildPost(r, cfg, KENH, IMG, VID, NEEDS_TITLE, $execution.id);\n  return b ? [{ json: b }] : [];\n}\nreturn [];\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-publish-now-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 - Bấm là chạy (webhook)\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\nNeeds the bound Apps Script: apps-script/Gate.gs",
        "height": 470,
        "width": 440,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -620,
        -220
      ],
      "id": "Hướng dẫn",
      "name": "Hướng dẫn"
    }
  ],
  "connections": {
    "Nhận lệnh từ Sheet": {
      "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
  }
}