{
  "id": "lqR2ivTOUP1U8YWq",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Special Tracker Share",
  "tags": [],
  "nodes": [
    {
      "id": "a9abe724-ec70-4aa9-9579-39968d9f624c",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -3000,
        40
      ],
      "parameters": {
        "path": "your-webhook-path",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "559432eb-1739-49e7-9fae-9337ef843239",
      "name": "message",
      "type": "n8n-nodes-base.set",
      "position": [
        -2540,
        20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "18602838-3e42-4804-b809-938dea492e38",
              "name": "body.events[0].message.text",
              "type": "string",
              "value": "={{ $json.body.events[0].message.text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1161ff82-4ceb-435a-a4ed-c2ca4f618627",
      "name": "image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2540,
        200
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $json.body.events[0].message.id }}/content ",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "afb32020-22d2-420c-9567-b3b81fda1075",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -2200,
        20
      ],
      "parameters": {
        "text": "=Please analyze {{ $json.body.events[0].message.text }} or the image",
        "options": {
          "systemMessage": "=Check relevance: If the text is not an expense record or invoice, immediately stop all processing. If it is bookkeeping-related, extract the six data fields below.\nExtract these six pieces of information: \n1. Date (If the input only says \u201ctoday\u201d, use {{ $now.format('YYYY-MM-DD') }}) 2. Channel (Free text from the input) 3. Channel Type (Must be exactly one of: Convenience Store, Personal Care Store, Hypermarket / Supermarket, Traditional Market, Online Shopping, Pharmacy, Hardware Store, Restaurant / Food, Stall, Medical Clinic / Hospital, 3C / Electronics Mall, Airline / Passenger Transport, Software Top-Up, Gas / Transit Top-Up, Online Course, Telecom Company) 4. Expense Description (Free text from the input) 5. Amount 6. Category (Must be exactly one of: Household, Main Meals, Drinks & Desserts, Household Items, Beauty, Clothing & Accessories, Transport, Entertainment, Telecom, Medical, 3C, Software, Learning, Travel)\nOutput format: JSON \uff1a{\\\"Date\\\": \\\"...\\\", \\\"Channel\\\": \\\"...\\\", \\\"Channel Type\\\": \\\"...\\\", \\\"Expense Description\\\": \\\"...\\\", \\\"Amount\\\": \\\"...\\\", \\\"Category\\\": \\\"...\\\"}\"\n\n",
          "passthroughBinaryImages": true
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2
    },
    {
      "id": "a4fe0a3b-40e0-4cf0-9d44-86abadca9fe4",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -2200,
        340
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "516361f7-21a9-4d9a-9f44-2bab05b9bfcb",
      "name": "reply_to_line",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        60
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\u2705 Expense recorded successfully: {{ $('Merge_all').item.json['for_duplication'] }}\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ab9fbffc-cd1e-4a2c-8f5e-23ee0f844c77",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1540,
        260
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 372483996,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit#gid=372483996",
          "cachedResultName": "2025en"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit?usp=drivesdk",
          "cachedResultName": "Spending Tracker"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6,
      "alwaysOutputData": true
    },
    {
      "id": "88bd5d9f-bcaa-4191-bcb6-1e3a0b138879",
      "name": "Merge_all",
      "type": "n8n-nodes-base.merge",
      "position": [
        -700,
        40
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2,
      "alwaysOutputData": false
    },
    {
      "id": "5d153710-d3a2-42dc-a090-c4faa3390210",
      "name": "reply_to_line_duplicated",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -200,
        280
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\u26a0\ufe0f This entry has already been logged and will not be duplicated\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "570de74c-b410-40ff-b7ac-781625c912a7",
      "name": "reply_to_line_no_spending",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -200,
        -120
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Irrelevant details or images will not be logged.\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "05789d97-3ebb-4e9f-951d-ecd54c4da404",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -2000,
        180
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"Date\": \"...\",\n  \"Channel\": \"...\",\n  \"Channel Type\": \"...\",\n  \"Expense Description\": \"...\",\n  \"Amount\": \"...\",\n  \"Category\": \"...\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "cbadff1d-15f5-4282-a62f-b42c25b41661",
      "name": "append_to_sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -200,
        60
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json.output.Date }}",
            "Amount": "={{ $json.output.Amount }}",
            "Channel": "={{ $json.output.Channel }}",
            "Category": "={{ $json.output.Category }}",
            "Channel Type": "={{ $json.output['Channel Type'] }}",
            "for_duplication": "={{ $json.for_duplication }}",
            "Expense Description": "={{ $json.output['Expense Description'] }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Channel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Channel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Channel Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Channel Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Expense Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Expense Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "for_duplication",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "for_duplication",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "for_duplication"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 372483996,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit#gid=372483996",
          "cachedResultName": "2025en"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit?usp=drivesdk",
          "cachedResultName": "Spending Tracker"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "aee3b5eb-ff29-4847-ab57-8cbb0cdba7a6",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -940,
        260
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "renameField": true,
              "outputFieldName": "dedupeList",
              "fieldToAggregate": "for_duplication"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8da264bd-5b13-4912-ae8e-7ded8fc6af17",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2540,
        -640
      ],
      "parameters": {
        "width": 720,
        "height": 540,
        "content": "## Prompt en\nCheck relevance: If the text is not an expense record or invoice, immediately stop all processing. If it is bookkeeping-related, extract the six data fields below.\nExtract these six pieces of information: \n1. Date (All numbers. If the input only says \u201ctoday\u201d, use {{ $now.format('YYYY-MM-DD') }}) 2. Channel (Free text from the input) 3. Channel Type (Must be exactly one of: Convenience Store, Personal Care Store, Hypermarket / Supermarket, Traditional Market, Online Shopping, Pharmacy, Hardware Store, Restaurant / Food, Stall, Medical Clinic / Hospital, 3C / Electronics Mall, Airline / Passenger Transport, Software Top-Up, Gas / Transit Top-Up, Online Course, Telecom Company) 4. Expense Description (Free text from the input) 5. Amount 6. Category (Must be exactly one of: Household, Main Meals, Drinks & Desserts, Household Items, Beauty, Clothing & Accessories, Transport, Entertainment, Telecom, Medical, 3C, Software, Learning, Travel)\nOutput format: JSON \uff1a{\\\"Date\\\": \\\"...\\\", \\\"Channel\\\": \\\"...\\\", \\\"Channel Type\\\": \\\"...\\\", \\\"Expense Description\\\": \\\"...\\\", \\\"Amount\\\": \\\"...\\\", \\\"Category\\\": \\\"...\\\"}\"\n\n## Prompt zh\n\u5206\u6790\u51fa\u8cc7\u8a0a\uff1a\n\u5148\u5224\u65b7\u662f\u5426\u70ba\u8a18\u5e33\u76f8\u95dc\u660e\u7d30\u3001\u767c\u7968\uff0c\u82e5\u4e0d\u662f\u5247\u4e0d\u9700\u8981\u8655\u7406\uff0c\u76f4\u63a5\u505c\u6b62\u6240\u6709\u6d41\u7a0b\u3002\u82e5\u662f\u8a18\u5e33\u76f8\u95dc\uff0c\u5206\u6790\u51fa\u516d\u500b\u8cc7\u8a0a:\n1. \u65e5\u671f\uff08\u5982\u679c\u53ea\u63d0\u5230'\u4eca\u5929'\uff0c\u7528{{ $now.format('YYYY-MM-DD') }} \uff09 2. \u901a\u8def 3. \u901a\u8def\u985e\u578b\uff08\u53ea\u80fd\u70ba\uff1a\u4fbf\u5229\u5546\u5e97\u3001\u500b\u4eba\u7528\u54c1\u5e97\u3001\u91cf\u8ca9\u8d85\u5e02\u3001\u50b3\u7d71\u5e02\u5834\u3001\u7db2\u8def\u8cfc\u7269\u3001\u85e5\u5c40\u3001\u4e94\u91d1\u767e\u8ca8\u3001\u9910\u5ef3\u5c0f\u5403\u5e97\u3001\u91ab\u7642\u9662\u6240\u30013C\u5546\u5834\u3001\u822a\u7a7a\u5ba2\u904b\u3001\u8edf\u9ad4\u5132\u503c\u3001\u52a0\u6cb9\u4ea4\u901a\u5132\u503c\u3001\u7dda\u4e0a\u8ab2\u7a0b\u3001\u96fb\u4fe1\u516c\u53f8\uff094. \u82b1\u8cbb\u660e\u7d30 5. \u91d1\u984d 6. \u985e\u5225\uff08\u53ea\u80fd\u70ba\uff1a\u5bb6\u7528\u3001\u6b63\u9910\u98f2\u98df\u3001\u98f2\u6599\u751c\u9ede\u3001\u751f\u6d3b\u7528\u54c1\u3001\u7f8e\u599d\u3001\u8863\u670d\u98fe\u54c1\u3001\u4ea4\u901a\u3001\u5a1b\u6a02\u3001\u96fb\u4fe1\u3001\u91ab\u7642\u30013C\u3001\u8edf\u9ad4\u3001\u5b78\u7fd2\u3001\u65c5\u904a\uff09\u3002\u82e5\u901a\u8def\u985e\u578b\u5224\u65b7\u70ba\u822a\u7a7a\u5ba2\u904b\uff0c\u985e\u5225\u4e00\u5b9a\u70ba\u65c5\u904a\u3002\u82e5\u7121\u6cd5\u5224\u65b7\u6216\u6c92\u8cc7\u8a0a\uff0c\u8acb\u586b\u5165'DN'\uff0c\u6bcf\u683c\u90fd\u8981\u6709\u8cc7\u6599\u3002\u8acb\u8f38\u51fa\u70ba JSON \u683c\u5f0f\uff0c\u4f8b\u5982\uff1a{\\\"\u65e5\u671f\\\": \\\"...\\\", \\\"\u901a\u8def\\\": \\\"...\\\", \\\"\u901a\u8def\u985e\u578b\\\": \\\"...\\\", \\\"\u82b1\u8cbb\u660e\u7d30\\\": \\\"...\\\", \\\"\u91d1\u984d\\\": \\\"...\\\", \\\"\u985e\u5225\\\": \\\"...\\\"}\"\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4f8f3594-5aa1-4279-b25e-502658e8bedc",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2100,
        440
      ],
      "parameters": {
        "width": 280,
        "height": 440,
        "content": "## Structured Output en\n{\n  \"Date\": \"...\",\n  \"Channel\": \"...\",\n  \"Channel Type\": \"...\",\n  \"Expense Description\": \"...\",\n  \"Amount\": \"...\",\n  \"Category\": \"...\"\n}\n\n## Structured Output zh\n{\n  \"\u65e5\u671f\": \"...\",\n  \"\u901a\u8def\": \"...\",\n  \"\u901a\u8def\u985e\u578b\": \"...\",\n  \"\u82b1\u8cbb\u660e\u7d30\": \"...\",\n  \"\u91d1\u984d\": \"...\",\n  \"\u985e\u5225\": \"...\"\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "efb13bc2-bc7a-4f6a-8ec1-d783ac9c1d41",
      "name": "deduplication",
      "type": "n8n-nodes-base.set",
      "position": [
        -1700,
        20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2f544a6e-2a51-4979-b0fb-ef9c3dcf54c6",
              "name": "for_duplication",
              "type": "string",
              "value": "={{ $json.output.Date }}-{{ $json.output['Channel Type'] }}-{{ $json.output.Amount }}-{{ $json.output.Category }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f6f35e3c-0424-4e7c-be0f-33e191dca2b4",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1700,
        -420
      ],
      "parameters": {
        "width": 320,
        "height": 300,
        "content": "## deduplication en\nfor_duplication = {{ $json.output.Date }}-{{ $json.output['Channel Type'] }}-{{ $json.output.Amount }}-{{ $json.output.Category }}\n\n## deduplication zh\n\u53bb\u91cd\u4f7f\u7528 = {{ $json.output['\u65e5\u671f'] }}-{{ $json.output['\u901a\u8def\u985e\u578b'] }}-{{ $json.output['\u91d1\u984d'] }}-{{ $json.output['\u985e\u5225'] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "296b9ff9-4819-49a8-b813-37dc507db57d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1760,
        440
      ],
      "parameters": {
        "color": 4,
        "width": 320,
        "height": 480,
        "content": "## Google Sheet Fields en\nSet up in advance\n1. Date\n2. Channel\n3. Channel Type\n4. Expense Description\n5. Amount\n6. Category\n7. for_duplication\n\n## Google Sheet Fields zh\n\u9810\u5148\u8a2d\u7f6e\u597d\n1. \u65e5\u671f\n2. \u901a\u8def\n3. \u901a\u8def\u985e\u578b\n4. \u82b1\u8cbb\u660e\u7d30\n5. \u91d1\u984d\n6. \u985e\u5225\n7. \u53bb\u91cd\u4f7f\u7528"
      },
      "typeVersion": 1
    },
    {
      "id": "8ff07d6b-6aa1-4a43-ab5a-231a4f668ca3",
      "name": "for_deduplications",
      "type": "n8n-nodes-base.set",
      "position": [
        -1240,
        260
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9b7c37f4-beac-493d-8cec-81d59fe144f7",
              "name": "for_duplication",
              "type": "string",
              "value": "={{ $json.for_duplication }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e1af076e-37f4-4cdf-8a9c-ca5f3cd2e743",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1380,
        440
      ],
      "parameters": {
        "width": 320,
        "height": 240,
        "content": "## for_deduplications en\nManual Mapping\nfor_deduplication = {{ $json['for_duplication'] }}\n\n## for_deduplications zh\nManual Mapping\n\u53bb\u91cd\u4f7f\u7528 = {{ $json['\u53bb\u91cd\u4f7f\u7528'] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "f62cb77b-74e1-4b90-9937-95af55e76497",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -940,
        440
      ],
      "parameters": {
        "width": 220,
        "height": 260,
        "content": "## Aggregrate en\nindividual field\ninput for_deduplication\noutput dedupeList\n\n## Aggregrate zh\nindividual field\ninput \u53bb\u91cd\u4f7f\u7528\noutput dedupeList"
      },
      "typeVersion": 1
    },
    {
      "id": "e2f1f377-4d78-4853-8d4e-037c4db5a483",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        -640
      ],
      "parameters": {
        "width": 540,
        "height": 540,
        "content": "## Switch en\n### empty\n{{ $('Merge_all').item.json['for_duplication'] }} = DN-DN-DN-DN\n{{ $('Merge_all').item.json['for_duplication'] }} matches regrx ^.*-DN-DN-DN$\n{{ $('Merge_all').item.json['for_duplication'] }} = ---\n### add\n{{ !$json.dedupeList.includes($json['for_duplication']) }} is true\n### duplicate\n{{ !$json.dedupeList.includes($json['for_duplication']) }} is false\n\n## Switch zh\n### empty\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} = DN-DN-DN-DN\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} matches regrx ^.*-DN-DN-DN$\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} = ---\n### add\n{{ !$json.dedupeList.includes($json['\u53bb\u91cd\u4f7f\u7528']) }} is true\n### duplicate\n{{ !$json.dedupeList.includes($json['\u53bb\u91cd\u4f7f\u7528']) }} is false"
      },
      "typeVersion": 1
    },
    {
      "id": "c9ea87a2-0f69-47a7-b409-1ea4ada2a666",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3240,
        -400
      ],
      "parameters": {
        "color": 4,
        "width": 400,
        "height": 300,
        "content": "## Requirements en\n1. Set up GCP OAuth and enable the Google Sheets API\n2. Pre-configure the Google Sheet field names\n3. Obtain the LINE Developer Webhook URL\n4. OpenAI API Key\n\n## \u9700\u6c42 zh\n1. \u8a2d\u7f6e GCP OAuth & \u958b\u555f google sheet API  \n2. \u9810\u5148\u8a2d\u7f6e\u597d Google Sheet field name\n3. \u53d6\u5f97 Line Developer Webhook URL \n4. Open AI API KEY"
      },
      "typeVersion": 1
    },
    {
      "id": "6a01a146-0b13-4ac5-8429-bf8452157587",
      "name": "Switch based on Expense Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        -2780,
        40
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "text",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "45419786-9466-49e9-bfe9-a46923cfe56e",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "text"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "image",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "dba6e722-8432-481b-aa6d-9a3fdff4f022",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "image"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "12f21b25-8a50-4224-8f3d-cfcba20510b6",
      "name": "Response Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -460,
        0
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8e802d98-e4ed-4e9b-a383-4e44229bce76",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "DN-DN-DN-DN"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "07afa184-938a-45fc-aa3f-69d9ea989058",
                    "operator": {
                      "type": "string",
                      "operation": "regex"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "^.*-DN-DN-DN$"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "01611be4-10af-43e2-99a4-00c96ea4f947",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "---"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "add",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "55f50acc-423c-4cf9-844a-a4a0e0c48973",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ !$json.dedupeList.includes($json['for_duplication']) }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "duplicate",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7cdabbfc-6a66-4f28-a2cf-781fe145faca",
                    "operator": {
                      "type": "boolean",
                      "operation": "false",
                      "singleValue": true
                    },
                    "leftValue": "={{ !$json.dedupeList.includes($json['for_duplication']) }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "none"
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "99a76ce1-eb41-4cb5-93a9-8bed26cf767d",
  "connections": {
    "image": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Switch based on Expense Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "message": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "deduplication",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Merge_all",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge_all": {
      "main": [
        [
          {
            "node": "Response Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "deduplication": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge_all",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Response Switch": {
      "main": [
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "append_to_sheet1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_duplicated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "append_to_sheet1": {
      "main": [
        [
          {
            "node": "reply_to_line",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "for_deduplications": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "for_deduplications",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Switch based on Expense Type": {
      "main": [
        [
          {
            "node": "message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}