{
  "id": "VBAdlukzjLaemKYa",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "LINE AI Handwritten Memo OCR & Tag Search System",
  "tags": [],
  "nodes": [
    {
      "id": "c63563b3-178e-43a6-8313-2662d4f582ec",
      "name": "LINE_Receive_Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -352,
        -352
      ],
      "parameters": {
        "path": "=e89b4943-1f2e-4d37-84ad-0fce0b78175e",
        "options": {},
        "httpMethod": "=POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "2485ae25-d2b6-4c64-91a5-18b83f160482",
      "name": "LINE_Download_ImageContent",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        -368
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $('LINE_Receive_Webhook').item.json.body.events[0].message.id }}/content",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{ $('Config_Set_Environment').item.json.LINE_ACCESS_TOKEN }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "7e255b30-152a-4a7a-904e-309c6ac4be42",
      "name": "Drive_Upload_Image",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1168,
        -368
      ],
      "parameters": {
        "name": "={{ $('LINE_Receive_Webhook').item.json.body.events[0].message.id }}.jpg",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1xR4KZbVauD6_J0t5YbPBvFSS1dxPLuWu",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1xR4KZbVauD6_J0t5YbPBvFSS1dxPLuWu",
          "cachedResultName": "LINE_PIC"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "e1486120-4929-4ecb-a838-c1cffdcbfa45",
      "name": "AI_OCR_Analyze_Image",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1568,
        -368
      ],
      "parameters": {
        "text": "=You are a high-precision OCR-dedicated engine.\n\nRead the text in the image as accurately as possible.\nIf the text cannot be read, always output the following JSON and terminate the analysis:\n{\n  \"title\": \"\",\n  \"summary\": \"Unable to read the text.\",\n  \"tags\":[]\n}\n\nIf the text is readable, create the following based on a summarized version of the content:\n\nTitle\nSummary\nTags (up to three, important keywords)\n\nRules:\n\nCategory should be a short, easily summarized word.\nSummary should be readable and within 200 characters.\nTags should be single words, with no duplicates.\nAvoid overly abstract tags (e.g., important, memo).\n\nImportant:\n\nOutput only pure JSON.\nDo not include any explanations or preambles.\nAbsolutely do not output any text other than JSON.\n\nOutput the following items in JSON format:\n{\n  \"title\": \"\",\n  \"summary\": \"\",\n  \"tags\":[\"\",\"\",\"\"]\n}\n\n",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "type": "HumanMessagePromptTemplate",
              "messageType": "imageBinary"
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "b58cc94f-97cd-4857-87c1-b312ebb9768e",
      "name": "AI_Model_Gemini",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1568,
        -208
      ],
      "parameters": {
        "options": {
          "topP": 1,
          "temperature": 0,
          "maxOutputTokens": 2048
        }
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2a32d0b4-2de6-461e-a432-0b5be1942010",
      "name": "Data_Parse_OCR_JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        1984,
        -368
      ],
      "parameters": {
        "jsCode": "let raw = $json[\"text\"] || \"\";\n\n// ```Remove JSON\nraw = raw.replace(/```json\\s*/i, \"\").replace(/```/g, \"\").trim();\n\n// Extract only the JSON part.\nconst match = raw.match(/\\{[\\s\\S]*\\}/);\nif (match) {\n  raw = match[0];\n}\n\n// Parse\nlet parsed;\ntry {\n  parsed = JSON.parse(raw);\n} catch (e) {\n  parsed = {\n    title: \"\",\n    summary: raw.slice(0, 100),\n    tags: []\n  };\n}\n\n// title\nif (!parsed.title || typeof parsed.title !== \"string\") {\n  parsed.title = \"NoTitle\";\n}\n\n// summary\nif (!parsed.summary || typeof parsed.summary !== \"string\") {\n  parsed.summary = \"NoSummary\";\n}\n\n// tags\nif (!Array.isArray(parsed.tags)) {\n  parsed.tags = [];\n}\n\n// Sanitize the contents of the tags as well\nparsed.tags = parsed.tags\n  .filter(tag => typeof tag === \"string\")\n  .slice(0, 3);\n\n// Original item\nconst item = $input.first();\n\n// Merge\nitem.json = {\n  ...item.json,\n  ...parsed\n};\n\n// Keep binary\nitem.binary = $input.first().binary;\n\nreturn [item];"
      },
      "typeVersion": 2
    },
    {
      "id": "7c8afb53-4d68-4855-b9ad-d666066b8092",
      "name": "AI_Check_OCR_Failure",
      "type": "n8n-nodes-base.if",
      "position": [
        2208,
        -368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0cad7233-7daa-4bff-bde7-08f254669175",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.summary }}",
              "rightValue": "=Unable to read the text."
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "a64dd875-873d-4af2-ad8c-bc928603bf0c",
      "name": "LINE_Reply_NoImage",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        720,
        560
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{$node['LINE_Receive_Webhook'].json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"If you send a handwritten memo, I will summarize its contents.\nYou can also search for memos containing a specific tag using #tagname.\nUse #taglist to display the list of tags.\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{$node[\"Config_Set_Environment\"].json.LINE_ACCESS_TOKEN }}"
            },
            {
              "name": "=Content-type",
              "value": "=application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "f6c5cf0f-764b-4a8a-88dd-f0e918897c04",
      "name": "Sheets_Append_Row",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2656,
        -352
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{$now.format(\"yyyy-MM-dd HH:mm:ss\")}}",
            "tags": "={{ $json.tags.join(\", \") }}",
            "title": "={{ $json.title }}",
            "webUrl": "={{ $('Drive_Upload_Image').item.json.webViewLink }}",
            "summary": "={{ $json.summary }}"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "webUrl",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "webUrl",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tags",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit#gid=0",
          "cachedResultName": "template"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit?usp=drivesdk",
          "cachedResultName": "OCR_data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "802e8d5c-49bb-4af0-a950-73cd2160f110",
      "name": "LINE_Push_Completion_Message",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3024,
        -352
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/push",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"to\":\"{{ $('LINE_Receive_Webhook').item.json.body.events[0].source.userId }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"The text has been summarized and saved. The title is\u300c{{$json.title}}\u300d,and the tags are\u300c{{ $json.tags }}\u300d\\nThe summary is\u300c{{ $json.summary }}\u300d\\n\\nYou can search for notes containing a specific tag using #tagname.\\nUse #taglist to display the list of tags.\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{ $('Config_Set_Environment').item.json.LINE_ACCESS_TOKEN }}"
            },
            {
              "name": "=Content-type",
              "value": "=application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "b8d79321-ca8b-402c-8606-c262dab9af48",
      "name": "Config_Set_Environment",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        -352
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "aa8b0c5c-df21-49f6-a885-eff8ad142d3d",
              "name": "LINE_ACCESS_TOKEN",
              "type": "string",
              "value": "=YOUR_ACCESS_TOKEN"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "01d5539d-d2c5-40b9-84a1-abf4696cfa4a",
      "name": "LINE_Reply_Processing",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        672,
        -368
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{$node['LINE_Receive_Webhook'].json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Processing\u2026 please wait a moment.\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{$node[\"Config_Set_Environment\"].json.LINE_ACCESS_TOKEN }}"
            },
            {
              "name": "=Content-type",
              "value": "=application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "76592edc-f37d-47e0-bfe4-77caec17ca67",
      "name": "LINE_Push_NoSummary",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2400,
        -480
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/push",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"to\":\"{{ $('LINE_Receive_Webhook').item.json.body.events[0].source.userId }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"The image could not be read.\\n\u30fbThe text may be too small.\\n\u30fbPlease retake the photo in a well-lit area.\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{ $('Config_Set_Environment').item.json.LINE_ACCESS_TOKEN }}"
            },
            {
              "name": "=Content-type",
              "value": "=application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "d339fe26-0f34-4838-9b2f-45583bfb12f7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        -608
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 400,
        "content": "## Detect message type\n\nThe workflow checks whether the incoming message is:\n\n\u2022 Image (handwritten memo)\n\u2022 Text command\n\nImage messages trigger the OCR pipeline.\n\nText messages are treated as commands such as tag search."
      },
      "typeVersion": 1
    },
    {
      "id": "3e7d1cd1-c2eb-4394-9a19-9db8a68e8a04",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        -672
      ],
      "parameters": {
        "width": 752,
        "height": 1440,
        "content": "## LINE AI Handwritten Memo OCR & Tag Search System\n\nThis workflow converts handwritten memo images sent via LINE into structured, searchable knowledge using AI.Users simply send a handwritten memo photo. The workflow automatically performs OCR, summarizes the content, generates tags, and stores the results in Google Sheets.Images are archived in Google Drive, allowing easy access to the original memo later.\n\n## Main Features\n\n\u2022 AI OCR for handwritten memo recognition  \n\u2022 Automatic title and summary generation  \n\u2022 Tag extraction for knowledge organization  \n\u2022 Image archiving in Google Drive  \n\u2022 Structured storage in Google Sheets  \n\u2022 Tag-based memo search via LINE  \n\u2022 Tag list generation for easy navigation\n\n## User Commands (via LINE)\n\nSend an image  \n\u2192 The memo is analyzed, summarized, tagged, and saved automatically.\n\n#tagname  \n\u2192 Searches memos that contain the specified tag.\n\n#taglist\n\u2192 Displays all tags currently stored in the database.\n\n## Workflow Steps\n\n1. Receive message from LINE via Webhook  \n2. Validate message type (image or command)  \n3. Save image to Google Drive  \n4. Run AI OCR and generate structured JSON  \n5. Parse and validate AI output safely  \n6. Store memo data in Google Sheets  \n7. Send completion message to the user\n\nIf OCR fails, the user receives guidance to retake the photo.\n\nStored Data Structure\n\nEach memo record includes:\n\n\u2022 Title  \n\u2022 Summary  \n\u2022 Tags  \n\u2022 Timestamp  \n\u2022 Link to the original image\n\n## Setup Requirements Before using this workflow:\n\n1. Create a LINE Messaging API channel\n2. Obtain a Channel Access Token\n3. Prepare a Google Sheet for memo storage\n4. Prepare a Google Drive folder for image storage\n5. Set credentials in the Config node\n\u30fbLINE API\n\n## Notes\n\n\u2022 OCR accuracy may vary depending on handwriting quality  \n\u2022 Very small or unclear text may not be recognized  \n\u2022 The workflow is optimized for quick memo capture and organization\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e93c4693-92f5-42d8-8bb3-d423a7f834ac",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 480,
        "content": "## Receive message from LINE \nThis workflow starts when a user sends a message to the LINE bot. \nThe LINE Messaging API sends an event to the webhook endpoint in n8n.\n\n The event contains:\n \u2022 userId \n \u2022 message type (image or text)\n \u2022 messageId\n\n This information is used to determine how the workflow should proceed."
      },
      "typeVersion": 1
    },
    {
      "id": "d1209241-8ef0-4fb2-80dd-f88c2684f6d4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -608
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 400,
        "content": "## Retrieve image from LINE\n\nIf the message is an image, the workflow retrieves the image file\nusing the LINE Messaging API.\n\nThe image binary data will be used for OCR processing and stored later."
      },
      "typeVersion": 1
    },
    {
      "id": "cf049c97-084e-48d3-aab5-b58dafb094cc",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        -608
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 400,
        "content": "## Save image to Google Drive\n\nThe memo image is stored in Google Drive for long-term access.\n\nThis allows users to open the original handwritten memo later\nfrom the stored link."
      },
      "typeVersion": 1
    },
    {
      "id": "f447bbfc-fbb7-4444-bd60-d4f375623776",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1536,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 560,
        "content": "## AI OCR processing\n\nThe uploaded image is analyzed using AI OCR.\n\nThe AI extracts:\n\n\u2022 memo title\n\u2022 summary\n\u2022 tags\n\u2022 main text\n\nThe result is returned as structured JSON."
      },
      "typeVersion": 1
    },
    {
      "id": "8c99d817-de3d-496b-b5ea-449d82e867cd",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 448,
        "content": "## Parse AI response\n\nThe JSON response from the AI is validated and parsed.\n\nThis step ensures the workflow receives structured data\nbefore saving it into the database."
      },
      "typeVersion": 1
    },
    {
      "id": "b0a1c12c-f0be-47eb-9cac-9c30dadc8d97",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2592,
        -608
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 416,
        "content": "## Store memo data\n\nThe memo data is stored in Google Sheets.\n\nEach record contains:\n\n\u2022 title\n\u2022 summary\n\u2022 tags\n\u2022 timestamp\n\u2022 image link."
      },
      "typeVersion": 1
    },
    {
      "id": "d7767fd5-8c2d-4dd2-8afe-ee2a062d8c2d",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2944,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 352,
        "content": "## Send confirmation message\n\nAfter the memo is successfully processed and stored,\na confirmation message is sent to the user via LINE.\n\nThe message confirms that the memo has been saved."
      },
      "typeVersion": 1
    },
    {
      "id": "512ee1b5-34a6-460c-9ac7-328ca4758a08",
      "name": "Extract_Tag",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        400
      ],
      "parameters": {
        "jsCode": "const text = $('LINE_Receive_Webhook').first().json.body.events[0].message.text;\n\n// #delete\nconst tag = text.replace(\"#\",\"\").trim();\n\nreturn [\n{\njson:{\ntag:tag,\n}\n}\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "9c13a744-ba39-4936-adb3-fd9cb7269856",
      "name": "Filter_By_Tag",
      "type": "n8n-nodes-base.code",
      "position": [
        1088,
        400
      ],
      "parameters": {
        "jsCode": "const searchTag = $items(\"Extract_Tag\")[0].json.tag;\n\nconst rows = $input.all();\n\nconst results = rows.filter(row => {\n\n  const tags = row.json.tags || \"\";\n\n  return tags.toLowerCase().includes(searchTag.toLowerCase());\n\n});\n\nreturn results.slice(0,5);"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "ec60432f-ef68-4ca9-aa4f-5e3e3fd0d14d",
      "name": "Build_Search_Result",
      "type": "n8n-nodes-base.code",
      "position": [
        1248,
        400
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Keep only the items that actually have data\nconst realItems = items.filter(item => item.json && (item.json.title || item.json.summary || (item.json.tags && item.json.tags.length)));\n\nlet payload;\n\nif (realItems.length === 0) {\n  payload = { message: \"No matching notes were found.\\\\nSend #taglist to see a list of available tags.\" };\n} else {\n  let text = \"Tag search results\\n\\n\";\n\n  realItems.forEach((item, i) => {\n    text += (i + 1) + \"\\n\";\n    text += \"Title:\" + (item.json.title || \"NoTitle\") + \"\\n\";\n    text += \"Summary:\" + (item.json.summary || \"NoSummary\") + \"\\n\";\n    text += \"tags:\" + (Array.isArray(item.json.tags) ? item.json.tags.join(\", \") : item.json.tags || \"\") + \"\\n\\n\";\n  });\n\n  payload = { message: text };\n}\n\nreturn [{ json: payload }];"
      },
      "typeVersion": 2
    },
    {
      "id": "39f0f8a1-9594-41e3-be33-49d84328ce82",
      "name": "LINE_Push_TagResults",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1440,
        400
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/push",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  to: $('LINE_Receive_Webhook').item.json.body.events[0].source.userId,\n  messages: [\n    {\n      type: \"text\",\n      text: $json.message\n    }\n  ]\n}) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{$node[\"Config_Set_Environment\"].json.LINE_ACCESS_TOKEN}}"
            },
            {
              "name": "=Content-Type",
              "value": "=application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "41507112-15c6-46c2-bad4-b4b010426457",
      "name": "Sheets_Get_All_Memos",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        896,
        400
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit#gid=0",
          "cachedResultName": "template"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit?usp=drivesdk",
          "cachedResultName": "OCR_data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b387017c-8dd2-40bc-b761-59898ca90d42",
      "name": "Restore_Image_Binary",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        -368
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\n\n// Restore binary\nitem.binary = $node[\"LINE_Download_ImageContent\"].binary;\n\nreturn [item];"
      },
      "typeVersion": 2
    },
    {
      "id": "faf8aece-42ef-4ced-92be-645eac3a09ab",
      "name": "Check_Image_Message",
      "type": "n8n-nodes-base.if",
      "position": [
        176,
        -352
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "58a4dbd8-8eed-400c-b94a-7083131e133a",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('LINE_Receive_Webhook').item.json.body.events[0].message.type }}",
              "rightValue": "image"
            },
            {
              "id": "69e0a667-2629-4538-9bb6-518274328f8b",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9d207368-4fb8-4a1e-941d-737f0af98b3c",
      "name": "Check_Tag_Command",
      "type": "n8n-nodes-base.if",
      "position": [
        528,
        416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a73b3ed7-0dbb-489a-9ed6-fdb55b4b9d3d",
              "operator": {
                "type": "string",
                "operation": "startsWith"
              },
              "leftValue": "={{ $('LINE_Receive_Webhook').item.json.body.events[0].message.text }}",
              "rightValue": "=#"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "50369319-eda2-4c0f-a7c3-350535c1274a",
      "name": "Check_Tag_List_Command",
      "type": "n8n-nodes-base.if",
      "position": [
        336,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "bfbc6b45-7602-4be0-a7a6-790755851292",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('LINE_Receive_Webhook').item.json.body.events[0].message.text }}",
              "rightValue": "#taglist"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c3962197-9330-45ce-b4ce-1478b37020d7",
      "name": "Build_TagList",
      "type": "n8n-nodes-base.code",
      "position": [
        688,
        -16
      ],
      "parameters": {
        "jsCode": "const rows = $input.all();\n\nlet tagSet = new Set();\n\nfor (const item of rows) {\n\n  const tags = item.json.tags || \"\";\n\n  tags.split(\",\")\n    .map(t => t.trim())\n    .filter(t => t !== \"\")\n    .forEach(t => tagSet.add(t));\n\n}\n\nconst tagList = Array.from(tagSet).sort();\n\nreturn [\n{\njson:{\ntagList: tagList,\ntagText: tagList.join(\"\\\\n\")\n}\n}\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "71d3cc39-0879-4e50-a36c-4a7276b34e19",
      "name": "LINE_Push_TagList",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        848,
        -16
      ],
      "parameters": {
        "url": "=https://api.line.me/v2/bot/message/push",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n \"to\": \"{{ $('LINE_Receive_Webhook').first().json.body.events[0].source.userId }}\",\n \"messages\": [\n  {\n   \"type\": \"text\",\n  \"text\": \"{{ '\u3010Taglist\u3011\\\\n' + $json.tagText }}\"\n   \n  }\n ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "=Authorization",
              "value": "=Bearer {{$node[\"Config_Set_Environment\"].json.LINE_ACCESS_TOKEN}}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "aeadf7a0-5214-4342-941c-5d85ec18bc5f",
      "name": "Sheets_Get_All_Memos_For_TagList",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        528,
        -16
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit#gid=0",
          "cachedResultName": "template"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eFUe4xD5Xgd5BbQ2kT_QzKQzF3EspnuXdpYYv8p46HM/edit?usp=drivesdk",
          "cachedResultName": "OCR_data"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2f114e7c-3479-407e-bdab-744b523f3bd4",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 1104,
        "height": 560,
        "content": "## Tag search command\n\nUsers can search memos by sending a tag command.\n\nExample\n#study\n#meeting\n\nThe workflow filters memos that contain the specified tag\nand returns the results to the user."
      },
      "typeVersion": 1
    },
    {
      "id": "24ace017-7bea-4a40-bcfa-82ee564de1aa",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        288,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 768,
        "height": 352,
        "content": "## Generate tag list\n\nThe workflow collects all stored tags\nand generates a unique tag list.\n\nThis helps users quickly see which topics\nare available in the memo database."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "c1c19ead-5e47-40d7-96af-79f1610b27f8",
  "connections": {
    "Extract_Tag": {
      "main": [
        [
          {
            "node": "Sheets_Get_All_Memos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build_TagList": {
      "main": [
        [
          {
            "node": "LINE_Push_TagList",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter_By_Tag": {
      "main": [
        [
          {
            "node": "Build_Search_Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI_Model_Gemini": {
      "ai_languageModel": [
        [
          {
            "node": "AI_OCR_Analyze_Image",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Check_Tag_Command": {
      "main": [
        [
          {
            "node": "Extract_Tag",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "LINE_Reply_NoImage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets_Append_Row": {
      "main": [
        [
          {
            "node": "LINE_Push_Completion_Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Drive_Upload_Image": {
      "main": [
        [
          {
            "node": "Restore_Image_Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build_Search_Result": {
      "main": [
        [
          {
            "node": "LINE_Push_TagResults",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check_Image_Message": {
      "main": [
        [
          {
            "node": "LINE_Reply_Processing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check_Tag_List_Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data_Parse_OCR_JSON": {
      "main": [
        [
          {
            "node": "AI_Check_OCR_Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI_Check_OCR_Failure": {
      "main": [
        [
          {
            "node": "LINE_Push_NoSummary",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets_Append_Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI_OCR_Analyze_Image": {
      "main": [
        [
          {
            "node": "Data_Parse_OCR_JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE_Receive_Webhook": {
      "main": [
        [
          {
            "node": "Config_Set_Environment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Restore_Image_Binary": {
      "main": [
        [
          {
            "node": "AI_OCR_Analyze_Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets_Get_All_Memos": {
      "main": [
        [
          {
            "node": "Filter_By_Tag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE_Reply_Processing": {
      "main": [
        [
          {
            "node": "LINE_Download_ImageContent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check_Tag_List_Command": {
      "main": [
        [
          {
            "node": "Sheets_Get_All_Memos_For_TagList",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check_Tag_Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config_Set_Environment": {
      "main": [
        [
          {
            "node": "Check_Image_Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE_Download_ImageContent": {
      "main": [
        [
          {
            "node": "Drive_Upload_Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets_Get_All_Memos_For_TagList": {
      "main": [
        [
          {
            "node": "Build_TagList",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}