{
  "id": "ARLGTXfk8OYbiUuF",
  "name": "Read palm lines and send report via LINE, Gemini, Gmail, and Google Calendar",
  "tags": [],
  "nodes": [
    {
      "id": "ecf2bdc5-9f23-4f25-90f6-b243f3b33ad7",
      "name": "Sticky Note \u2014 Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1392,
        -736
      ],
      "parameters": {
        "width": 644,
        "height": 680,
        "content": "## \ud83d\udd2e Palm Reading Bot \u2014 Overview\n\n### How it works\nThis workflow turns your LINE account into an AI-powered palm reading bot. When a user sends a palm photo via LINE, the image is downloaded and analyzed by Google Gemini, which identifies the life line, heart line, head line, and fate line. The results are split into three outputs: a short reply sent back to LINE, a detailed HTML report delivered by Gmail, and up to three lucky days automatically registered in Google Calendar.\n\n### Setup steps\n1. Add your LINE Channel Access Token to the **Set config variables** node\n2. Set your Gmail address for both `GMAIL_FROM` and `GMAIL_TO`\n3. Set your Google Calendar ID (usually your Gmail address)\n4. Connect your Google Gemini API credential to the **Google Gemini Chat Model** node\n5. Connect your Gmail OAuth2 credential to the **Send detailed report via Gmail** node\n6. Connect your Google Calendar OAuth2 credential to the **Register lucky days in Google Calendar** node\n7. Set the LINE webhook URL in your LINE Developer Console to point to this workflow's webhook URL\n\n### Customization\n- Edit the Gemini prompt in **Analyze palm lines with Gemini** to change the reading style or language\n- Swap Gmail for any email node to match your mail provider\n- Adjust the calendar event timing (default: 7 days from now) in the Calendar node"
      },
      "typeVersion": 1
    },
    {
      "id": "3ca2e7f6-2740-4126-997a-a646877ee45d",
      "name": "Sticky Note \u2014 Trigger section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "height": 200,
        "content": "## 1\ufe0f\u20e3 Trigger & config\n\nReceives the LINE webhook POST and immediately sets all credentials and event data into named fields for easy reference downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "4731322d-918f-449c-9a6b-34fbb3697729",
      "name": "Sticky Note \u2014 Image check section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "height": 200,
        "content": "## 2\ufe0f\u20e3 Image check\n\nOnly image messages are supported. Text or sticker messages are caught here and receive a friendly prompt asking the user to send a photo of their palm instead."
      },
      "typeVersion": 1
    },
    {
      "id": "47fcf89b-38c7-4c87-8ca6-e175a6b1676a",
      "name": "Sticky Note \u2014 AI analysis section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "height": 200,
        "content": "## 3\ufe0f\u20e3 AI analysis\n\nThe palm image is fetched from LINE's content API and passed to Google Gemini. The prompt asks for a LINE-ready short summary, a full line-by-line reading, and three lucky date suggestions in YYYY-MM-DD format."
      },
      "typeVersion": 1
    },
    {
      "id": "485b33ad-9be9-4b13-aeb6-18d83a462d29",
      "name": "Sticky Note \u2014 Output section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        336,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 340,
        "height": 200,
        "content": "## 4\ufe0f\u20e3 Multi-channel output\n\nAfter parsing the Gemini response, three actions run in parallel:\n- Short summary \u2192 LINE reply\n- Full report \u2192 Gmail HTML email\n- Lucky dates \u2192 Google Calendar events"
      },
      "typeVersion": 1
    },
    {
      "id": "f5e404ac-3783-436d-8271-4be167696fc4",
      "name": "Receive LINE webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -928,
        0
      ],
      "parameters": {
        "path": "line-webhook",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "e5ff02ca-3b1c-475d-9e34-13883f505c81",
      "name": "Set config variables",
      "type": "n8n-nodes-base.set",
      "position": [
        -704,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "set_line_channel_access_token",
              "name": "LINE_CHANNEL_ACCESS_TOKEN",
              "type": "string",
              "value": "YOUR_LINE_CHANNEL_ACCESS_TOKEN"
            },
            {
              "id": "set_gmail_from",
              "name": "GMAIL_FROM",
              "type": "string",
              "value": "YOUR_GMAIL_ADDRESS"
            },
            {
              "id": "set_gmail_to",
              "name": "GMAIL_TO",
              "type": "string",
              "value": "YOUR_GMAIL_ADDRESS"
            },
            {
              "id": "set_calendar_id",
              "name": "GOOGLE_CALENDAR_ID",
              "type": "string",
              "value": "YOUR_GOOGLE_CALENDAR_ID"
            },
            {
              "id": "set_line_reply_token",
              "name": "replyToken",
              "type": "string",
              "value": "={{ $json.body.events[0].replyToken }}"
            },
            {
              "id": "set_message_type",
              "name": "messageType",
              "type": "string",
              "value": "={{ $json.body.events[0].message.type }}"
            },
            {
              "id": "set_image_id",
              "name": "imageId",
              "type": "string",
              "value": "={{ $json.body.events[0].message.id }}"
            },
            {
              "id": "set_user_id",
              "name": "userId",
              "type": "string",
              "value": "={{ $json.body.events[0].source.userId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "194e3e36-2d22-494e-a055-c61003b59815",
      "name": "Check if message is image",
      "type": "n8n-nodes-base.if",
      "position": [
        -480,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond_image",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.messageType }}",
              "rightValue": "image"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2aec9fc6-0155-4642-8b58-96c52fe80158",
      "name": "Download image from LINE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -256,
        -112
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $json.imageId }}/content",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.LINE_CHANNEL_ACCESS_TOKEN }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a241333c-071c-4473-a6c2-9655ed11e8c5",
      "name": "Analyze palm lines with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -80,
        -112
      ],
      "parameters": {
        "text": "=You are a professional palm reader. Analyze the palm image and output the following three sections.\n\n[LINE reply (within 100 characters)]\nA short message sharing the first impression of the palm lines.\n\n[Detailed report]\n- Life line: characteristics and health fortune\n- Heart line: characteristics and love fortune\n- Head line: characteristics and career fortune\n- Fate line: characteristics and overall fortune\n- Summary\n\n[Lucky day suggestions]\nSuggest 3 lucky days best suited for this person in YYYY-MM-DD format. Add a brief reason for each.",
        "messages": {
          "messageValues": [
            {
              "type": "HumanMessagePromptTemplate",
              "messageType": "imageBinary"
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "c798fb24-5309-4d4d-9b14-f13c73a11970",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -32,
        64
      ],
      "parameters": {
        "options": {
          "temperature": 0.7,
          "maxOutputTokens": 1500
        }
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ef61e224-1106-4441-9eaa-fc065bbed42b",
      "name": "Parse Gemini response",
      "type": "n8n-nodes-base.set",
      "position": [
        192,
        -112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "parse_gemini",
              "name": "geminiText",
              "type": "string",
              "value": "={{ $json.text }}"
            },
            {
              "id": "parse_line_msg",
              "name": "lineMessage",
              "type": "string",
              "value": "=\ud83d\udd2e Palm Reading Result\\n\\n{{ $json.text.match(/\\[LINE reply[^\\]]*\\]([\\s\\S]*?)(?=\\[|$)/)?.[1]?.trim() ?? 'Your palm has been read! Check your email for the full report \u2728' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "cc4f6eea-8813-484e-84df-bf8cbcf88fc3",
      "name": "Reply to LINE with summary",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        -240
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Set config variables').item.json.replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"{{ $json.lineMessage }}\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set config variables').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "78153381-a5df-4d11-baa1-c8c0fb9fe7e6",
      "name": "Send detailed report via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        416,
        -80
      ],
      "parameters": {
        "sendTo": "={{ $('Set config variables').item.json.GMAIL_TO }}",
        "message": "=<h2>\ud83d\udd2e Palm Reading Detailed Report</h2>\n<p>Thank you for using the palm reading service.</p>\n<hr>\n<pre style=\"font-family: sans-serif; white-space: pre-wrap;\">{{ $('Parse Gemini response').item.json.geminiText }}</pre>\n<hr>\n<p style=\"color: gray; font-size: 12px;\">\u203b This email was sent automatically via n8n workflow.</p>",
        "options": {
          "senderName": "Palm Reading Bot"
        },
        "subject": "\ud83d\udd2e Palm Reading Detailed Report"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "990021c1-c0e2-4276-97c3-aa01ad1417fb",
      "name": "Register lucky days in Google Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        416,
        80
      ],
      "parameters": {
        "end": "={{ $now.plus(7, 'days').plus(1, 'hours').toISO() }}",
        "start": "={{ $now.plus(7, 'days').toISO() }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set config variables').item.json.GOOGLE_CALENDAR_ID }}"
        },
        "additionalFields": {
          "description": "=Lucky day suggested by your palm reading \ud83c\udf1f\\n\\n{{ $('Parse Gemini response').item.json.geminiText.match(/\\[Lucky day suggestions\\]([\\s\\S]*?)(?=\\[|$)/)?.[1]?.trim() ?? 'Lucky day from palm reading' }}"
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "33cd1525-cdc7-43b9-8e39-7f06d77b2147",
      "name": "Reply with error for non-image message",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -256,
        128
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Set config variables').item.json.replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\ud83d\ude4f Please send a photo of your palm!\\nThis bot only supports image messages.\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set config variables').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "fd8a87d7-b279-48a6-9fe6-51986616cead",
      "name": "Respond to webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        640,
        -80
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "OK"
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "b3b8bead-8ffe-4a92-a775-577e80143530",
  "connections": {
    "Receive LINE webhook": {
      "main": [
        [
          {
            "node": "Set config variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set config variables": {
      "main": [
        [
          {
            "node": "Check if message is image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gemini response": {
      "main": [
        [
          {
            "node": "Reply to LINE with summary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send detailed report via Gmail",
            "type": "main",
            "index": 0
          },
          {
            "node": "Register lucky days in Google Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download image from LINE": {
      "main": [
        [
          {
            "node": "Analyze palm lines with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Analyze palm lines with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Check if message is image": {
      "main": [
        [
          {
            "node": "Download image from LINE",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reply with error for non-image message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply to LINE with summary": {
      "main": [
        [
          {
            "node": "Respond to webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze palm lines with Gemini": {
      "main": [
        [
          {
            "node": "Parse Gemini response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send detailed report via Gmail": {
      "main": [
        [
          {
            "node": "Respond to webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Register lucky days in Google Calendar": {
      "main": [
        [
          {
            "node": "Respond to webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}