{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "4b498bde-7310-41c3-8de7-bb9ad947e4e2",
      "name": "Sticky Note 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 644,
        "height": 376,
        "content": "## 1. Data Processing\n1. Extract data/topic from LINE.\n2. **Gemini acts as Data Analyst**.\n3. Structures the data into a visual description (Charts, Icons, Layout)."
      },
      "typeVersion": 1
    },
    {
      "id": "d30ac12e-af92-49b4-8f70-28f8b910082b",
      "name": "Sticky Note 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 1012,
        "height": 376,
        "content": "## 2. Infographic Rendering\n1. Submit to Nano Banana Pro.\n2. **Configured for 3:4 Aspect Ratio**.\n3. **Smart Loop:** Checks status every 5s until ready."
      },
      "typeVersion": 1
    },
    {
      "id": "ed88f242-9e86-4933-aa6c-a8fbb335d847",
      "name": "Sticky Note 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1856,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 860,
        "height": 376,
        "content": "## 3. Host & Deliver\n1. Download the generated Infographic.\n2. Upload to S3 (Public).\n3. Send the visual report back to LINE."
      },
      "typeVersion": 1
    },
    {
      "id": "85af1e9b-e708-42de-997b-390ba1b473e8",
      "name": "Main Description",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        -32
      ],
      "parameters": {
        "width": 300,
        "height": 528,
        "content": "# AI Infographic Generator\n\nTurns complex data or topics into easy-to-understand **Infographics** automatically.\n\n### Key Features\n- **Role:** Data Visualization Expert.\n- **Format:** 3:4 Vertical Poster.\n- **Engine:** Nano Banana Pro.\n\n### How it works\n1. **Input:** User sends data (e.g., \"Japan Energy Mix: 20% Solar...\").\n2. **Design:** Gemini decides the best chart type (Pie, Bar, Flowchart).\n3. **Render:** Nano Banana creates the visual graphic.\n4. **Deliver:** Image sent via LINE."
      },
      "typeVersion": 1
    },
    {
      "id": "adcafd67-eebf-4ef6-a953-0042eaf75dc2",
      "name": "LINE Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        144,
        128
      ],
      "parameters": {
        "path": "infographic-v7158",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "7bb61745-1574-41dc-94d8-0f9bb44747e1",
      "name": "Extract Data",
      "type": "n8n-nodes-base.code",
      "position": [
        320,
        128
      ],
      "parameters": {
        "jsCode": "// LINE Webhook parsing\nconst body = items[0].json.body;\n\nif (!body.events || !Array.isArray(body.events) || !body.events[0]) {\n  throw new Error(\"No LINE events found\");\n}\n\nconst event = body.events[0];\n\nreturn [\n  {\n    json: {\n      replyToken: event.replyToken,\n      userId: event.source.userId,\n      message: event.message.text,\n      timestamp: event.timestamp\n    }\n  }\n];"
      },
      "typeVersion": 1
    },
    {
      "id": "6981c521-0750-4d7d-b0df-6b77448a4e1f",
      "name": "Optimize Prompt (Data Vis)",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        496,
        128
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.0-flash-lite",
          "cachedResultName": "models/gemini-2.0-flash-lite"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "\"You are an expert Information Designer and Data Visualization Specialist.\\nThe user will send you a topic or a set of data points.\\nYour goal is to write a detailed prompt for Nano Banana Pro to generate a Professional INFOGRAPHIC (3:4 aspect ratio).\\n\\nIMPORTANT RULES:\\n1. Analyze the data/topic. Choose the best visualization method (Pie Chart, Bar Graph, Process Flow, or Timeline).\\n2. Layout: Describe a clean, structured layout with a clear Title Header, Data Sections, and Icons.\\n3. Visual Style: Modern, clean, vector-style flat design or 3D infographic style. Use a professional color palette suitable for the topic.\\n4. Text: Instruct the AI to render the key numbers and labels clearly.\\n5. Output ONLY the English prompt text, nothing else. NO DOUBLE QUOTES.\""
            },
            {
              "content": "={{ $json.message }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "typeVersion": 1
    },
    {
      "id": "1b9e4427-db85-4905-ab67-c6440c8a16aa",
      "name": "Parse Gemini Response",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        128
      ],
      "parameters": {
        "jsCode": "const response = items[0].json;\nlet promptText = \"\";\nlet text = \"\";\n\nif (response.candidates && response.candidates[0] && response.candidates[0].content && response.candidates[0].content.parts) {\n  text = response.candidates[0].content.parts[0].text;\n} \nelse if (response.content && response.content.parts && response.content.parts[0]) {\n  text = response.content.parts[0].text;\n}\nelse if (response.output) {\n  text = response.output;\n}\n\nif (text) {\n  text = text.replace(/```json/g, \"\").replace(/```/g, \"\").trim();\n  try {\n    const parsed = JSON.parse(text);\n    if (Array.isArray(parsed) && parsed.length > 0) {\n      const firstItem = parsed[0];\n      if (typeof firstItem === 'object' && firstItem !== null && firstItem.prompt) {\n        promptText = firstItem.prompt; \n      } else if (typeof firstItem === 'string') {\n        promptText = firstItem;\n      } else {\n        promptText = JSON.stringify(firstItem);\n      }\n    } else if (typeof parsed === 'object' && parsed !== null) {\n       if (parsed.prompt) { promptText = parsed.prompt; } else { promptText = JSON.stringify(parsed); }\n    } else {\n      promptText = text; \n    }\n  } catch (e) {\n    promptText = text.replace(/^\\[|\\]$/g, '').replace(/^[\\\"']|[\\\"']$/g, '').replace(/\\\\n/g, '').trim();\n  }\n}\n\nif (!promptText || typeof promptText !== 'string') { promptText = String(promptText || \"\"); }\npromptText = promptText.replace(/\\\"/g, \"'\").replace(/\\n/g, \" \").replace(/\\r/g, \" \").replace(/\\s+/g, \" \").trim();\n\nreturn [{\n  json: {\n    prompt: promptText,\n    userId: $('Extract Data').item.json.userId || \"unknown_user\",\n    taskId: response.taskId || null\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "eecb8e0c-3dc9-4847-bbdb-84d3d106ec0b",
      "name": "Submit to Nano Banana Pro",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1056,
        128
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/jobs/createTask",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"nano-banana-pro\",\n  \"input\": {\n    \"prompt\": \"{{ $json.prompt }}\",\n    \"aspect_ratio\": \"3:4\",\n    \"resolution\": \"2K\",\n    \"output_format\": \"png\"\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "b253a76e-133a-417d-a76b-16c0df8abd1c",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1248,
        128
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "2dc495ab-5754-49f0-8dae-127aaa153736",
      "name": "Check Job Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1456,
        128
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/jobs/recordInfo",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $json.data.taskId }}"
            },
            {
              "name": "recordId",
              "value": "={{ $json.data.recordId }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b3ec2fb1-e111-40c7-a7b1-bc76abaadba7",
      "name": "Is Ready?",
      "type": "n8n-nodes-base.if",
      "position": [
        1648,
        128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2eb42234-291b-4803-b446-87282037f82b",
              "operator": {
                "name": "filter-state",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.state }}",
              "rightValue": "success"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b22257b2-7915-42e5-8ac7-2ce7c560896e",
      "name": "Parse Result",
      "type": "n8n-nodes-base.code",
      "position": [
        1904,
        112
      ],
      "parameters": {
        "jsCode": "// Parse response and extract image URL\nconst response = items[0].json;\nconst data = Array.isArray(response) ? response[0] : response;\n\n// Asumimos que si llegamos aqui es porque el estado es success (verificado por el nodo IF)\nif (data.data && data.data.resultJson) {\n  try {\n    const resultData = JSON.parse(data.data.resultJson);\n    const resultUrls = resultData.resultUrls || [];\n    if (resultUrls.length > 0) {\n      return [{\n        json: {\n          imageUrl: resultUrls[0],\n          taskId: data.data.taskId,\n          status: 'completed'\n        }\n      }];\n    }\n  } catch (e) {\n    throw new Error('Failed to parse resultJson');\n  }\n}\n\nthrow new Error('No image found in response');"
      },
      "typeVersion": 1
    },
    {
      "id": "c8a1d37a-1a8e-4baa-be93-aaf1eb309896",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2112,
        112
      ],
      "parameters": {
        "url": "={{ $json.imageUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "a25e24b7-7bba-41d5-96bc-93ab6e646dff",
      "name": "Upload to S3",
      "type": "n8n-nodes-base.awsS3",
      "position": [
        2304,
        112
      ],
      "parameters": {
        "fileName": "={{ 'infographic-' + Date.now() + '.png' }}",
        "operation": "upload",
        "bucketName": "banners-bot-v7158",
        "additionalFields": {}
      },
      "typeVersion": 2
    },
    {
      "id": "57501e89-72ba-4ae5-8264-0ddefb5343cf",
      "name": "Send Image to LINE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2512,
        112
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n \"replyToken\": \"{{ $('LINE Webhook').item.json.replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"image\",\n      \"originalContentUrl\": \"{{ $('Upload to S3').item.json.Location }}\",\n      \"previewImageUrl\": \"{{ $('Upload to S3').item.json.Location }}\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    }
  ],
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Check Job Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Ready?": {
      "main": [
        [
          {
            "node": "Parse Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data": {
      "main": [
        [
          {
            "node": "Optimize Prompt (Data Vis)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE Webhook": {
      "main": [
        [
          {
            "node": "Extract Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Result": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to S3": {
      "main": [
        [
          {
            "node": "Send Image to LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Upload to S3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Job Status": {
      "main": [
        [
          {
            "node": "Is Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gemini Response": {
      "main": [
        [
          {
            "node": "Submit to Nano Banana Pro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit to Nano Banana Pro": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Optimize Prompt (Data Vis)": {
      "main": [
        [
          {
            "node": "Parse Gemini Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}