AutomationFlowsAI & RAG › Get Multimodal AI Dating Advice on Telegram with Google Gemini and Sheets

Get Multimodal AI Dating Advice on Telegram with Google Gemini and Sheets

ByJohn Alejandro SIlva @alejandro-silva on n8n.io

Rizz AI is not just a chatbot; it's a full-featured, AI-powered CRM for your dating life.

Event trigger★★★★★ complexityAI-powered41 nodesTelegramGoogle GeminiGoogle Gemini ChatMemory Buffer WindowTelegram TriggerGoogle SheetsAgentGoogle Sheets Tool
AI & RAG Trigger: Event Nodes: 41 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #13446 — we link there as the canonical source.

This workflow follows the Agent → Googlegemini recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateId": "7756",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "c142f5dd-fbeb-4b25-a0fe-f684546e6eff",
      "name": "Download Voice Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2144,
        1072
      ],
      "parameters": {
        "fileId": "={{ $('Telegram Trigger').item.json.message.voice.file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "68f15a08-cbe2-4ad9-9cfe-8b7a7c60787c",
      "name": "get_message (text)",
      "type": "n8n-nodes-base.set",
      "position": [
        2144,
        912
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "801ec600-22ad-4a94-a2b4-ae72eb271df0",
              "name": "message",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.text }}"
            },
            {
              "id": "263071fb-bcdf-42b0-bb46-71b75fa0bf2a",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b975dcce-68d8-4e3d-b50e-330f3f63812d",
      "name": "Analyze image",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "maxTries": 3,
      "position": [
        2368,
        1248
      ],
      "parameters": {
        "text": "=You are a Dating Forensics & Social Dynamics Expert. Think like a high-end dating coach and behavioral analyst. Reason silently and do not reveal your steps. From a single screenshot (Dating App Profile or Chat Interface), identify the context, extract key data, and analyze the \"vibe\" to give a strategic summary.\n\nAnalysis method (internal only; do not output these steps)\n\n1. Identify Context: Is this a Profile (Tinder/Bumble/Hinge bio) or a Conversation (WhatsApp/iMessage/App Chat)?\n2. Extract Identity: Look for the name in the header (top center). If ambiguous, look for the name in the text.\n3. Extract Data (Profile):\n   - Age, Job, School, Distance.\n   - Bio text (OCR).\n   - Visual Cues: Analyze photos for hobbies (gym, travel, pets), style (casual, formal), and red/green flags.\n4. Extract Data (Chat):\n   - Identify the \"Other Person\" (gray bubbles/left side) vs \"Me\" (blue/green bubbles/right side).\n   - Transcribe the last 3-4 exchanges to understand the current flow.\n   - Determine Sentiment: Is it dry, flirty, angry, or eager?\n5. Formulate Strategy: Based on the data, what is the best opening line or reply?\n\nOutput rules (must follow exactly)\n\n- Plain text only.\n- Use this exact structure and field order.\n- No markdown formatting (no bolding, no italics), no JSON.\n- If a field is missing, write \"Unknown\".\n\nContext: [Profile or Chat]\nName: [Name of the match]\nDetails: [Age, Job, or Platform]\nContent: [The Bio text OR The transcript of the last few messages]\nVisuals: [Description of photos/vibe OR \"N/A\" if chat]\nSentiment: [Cold/Warm/Hot/Neutral]\nSuggested Angle: [One sentence tactical advice]",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3-flash-preview",
          "cachedResultName": "models/gemini-3-flash-preview"
        },
        "options": {},
        "resource": "image",
        "inputType": "binary",
        "operation": "analyze"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 2000
    },
    {
      "id": "04b43855-1600-424d-a2ae-458fb72a0525",
      "name": "Analyze voice message",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        2368,
        1072
      ],
      "parameters": {
        "text": "What's in this audio message from telegram user?",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3-flash-preview",
          "cachedResultName": "models/gemini-3-flash-preview"
        },
        "options": {},
        "resource": "audio",
        "inputType": "binary",
        "operation": "analyze"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3d5d43cc-4639-4d46-a095-b6de79d3aede",
      "name": "get_message (Audio/Video message)",
      "type": "n8n-nodes-base.set",
      "position": [
        2544,
        1072
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d8935452-fe20-469d-a68d-1aad056cb8dd",
              "name": "message",
              "type": "string",
              "value": "=Voice message description:{{ $json.candidates?.[0]?.content?.parts?.[0]?.text || $json.content?.parts?.[0]?.text }}"
            },
            {
              "id": "93f1bba1-1180-404a-93ca-c34cf1d1b7ac",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "60e92d69-3fd6-4311-ba91-ea445adf9727",
      "name": "get_message (Media  message)",
      "type": "n8n-nodes-base.set",
      "position": [
        2544,
        1248
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d8935452-fe20-469d-a68d-1aad056cb8dd",
              "name": "message",
              "type": "string",
              "value": "=Content:\n{{ $json.content.parts[0].text }}"
            },
            {
              "id": "53e34499-7dad-4f94-aa7d-f778321f13f4",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "563fd16d-25d2-4496-94eb-d6a8be600e92",
      "name": "Typing\u2026",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1264,
        960
      ],
      "parameters": {
        "chatId": "={{ $json.message.chat.id }}",
        "operation": "sendChatAction"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "29051108-72c1-49da-8b51-25350affe0de",
      "name": "get_error_message1",
      "type": "n8n-nodes-base.set",
      "position": [
        2144,
        1408
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d8935452-fe20-469d-a68d-1aad056cb8dd",
              "name": "message",
              "type": "string",
              "value": "=It was not possible to process the file.File type not supported."
            },
            {
              "id": "38ba2498-2141-4a04-a22a-64563fe2ee6f",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "41cf82ed-b4f4-4224-93b2-e2c96f4c0f4c",
      "name": "Send a text message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3600,
        1184
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "MarkdownV2",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "938dbd1b-a566-4bcf-9c9c-7abf90174f8b",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        3024,
        1056
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-3-flash-preview"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ad29c874-9686-41aa-af26-5db22169f1dd",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        3344,
        1072
      ],
      "parameters": {
        "sessionKey": "={{ $json.chat_id }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "typeVersion": 1.3
    },
    {
      "id": "dcbd0b50-3cb5-4e3e-be26-026199676bb7",
      "name": "MarkdownV2",
      "type": "n8n-nodes-base.code",
      "position": [
        3440,
        1184
      ],
      "parameters": {
        "jsCode": "/**\n * MarkdownV2-safe formatter + auto-chunker for Telegram (n8n Code node)\n * --------------------------------------------------------------------\n * - Allows: *bold*, _italic_, ||spoiler||, [label](url)\n * - Escapes everything else for Telegram MarkdownV2\n * - Validates/normalizes URLs\n * - Converts \"# Heading\" lines to bold titles\n * - Splits long messages into <= 4096-char chunks (uses a 4000-char budget)\n * - Outputs one item per chunk so the Telegram node sends all parts\n *\n * Recommended: Run this node in \"Run Once for All Items\".\n */\n\nconst MAX_TELEGRAM = 4096;\nconst SAFE_BUDGET = 4000; // small margin to avoid edge overflows\n\n// ============ MarkdownV2 helpers ============\nfunction escapeMarkdownV2(text) {\n  if (!text) return '';\n  return String(text).replace(/([\\\\_*[\\]()~`>#+\\-=|{}.!])/g, '\\\\$1');\n}\n\nfunction escapeForUrl(url) {\n  return String(url).replace(/[)\\\\]/g, '\\\\$&');\n}\n\nfunction normalizeAndValidateUrl(url) {\n  let raw = String(url || '').trim();\n  try {\n    const u = new URL(raw);\n    return u.toString();\n  } catch {}\n  // Try https:// for bare domains\n  const domainLike = /^[a-z0-9.-]+\\.[a-z]{2,}([/:?#].*)?$/i.test(raw);\n  if (domainLike) {\n    try {\n      const u2 = new URL('https://' + raw);\n      return u2.toString();\n    } catch {}\n  }\n  return null;\n}\n\nfunction normalizeHeadings(text) {\n  // Turn \"# Title\" \u2192 \"*Title*\"\n  return text.replace(/^(#{1,6})\\s+(.*)$/gm, (m, hashes, title) => `*${title.trim()}*`);\n}\n\nfunction normalizeCommonMd(text) {\n  return String(text)\n    .replace(/\\*\\*([\\s\\S]*?)\\*\\*/g, '*$1*') // **bold** \u2192 *bold*\n    .replace(/__([\\s\\S]*?)__/g, '_$1_');    // __italic__ \u2192 _italic_\n}\n\n/**\n * Convert incoming text to Telegram-safe MarkdownV2.\n */\nfunction processMarkdownV2Safe(inputText) {\n  if (!inputText) return '';\n\n  let text = normalizeCommonMd(String(inputText));\n  text = normalizeHeadings(text);\n\n  const placeholders = { links: [], bolds: [], italics: [], spoilers: [] };\n\n  // Links: keep safe via placeholders during escaping\n  text = text.replace(/\\[([^\\]\\n]+)\\]\\(([^)]+)\\)/g, (m, label, url) => {\n    const normalizedUrl = normalizeAndValidateUrl(url);\n    if (!normalizedUrl) return escapeMarkdownV2(label);\n    const idx = placeholders.links.length;\n    const ph = `\u27ecL${idx}\u27ed`;\n    const safeLabel = escapeMarkdownV2(label);\n    const safeUrl = escapeForUrl(normalizedUrl);\n    placeholders.links.push(`[${safeLabel}](${safeUrl})`);\n    return ph;\n  });\n\n  // Bold\n  text = text.replace(/\\*([\\s\\S]+?)\\*/g, (m, inner) => {\n    const idx = placeholders.bolds.length;\n    const ph = `\u27ecB${idx}\u27ed`;\n    placeholders.bolds.push(`*${escapeMarkdownV2(inner)}*`);\n    return ph;\n  });\n\n  // Italic\n  text = text.replace(/_([\\s\\S]+?)_/g, (m, inner) => {\n    const idx = placeholders.italics.length;\n    const ph = `\u27ecI${idx}\u27ed`;\n    placeholders.italics.push(`_${escapeMarkdownV2(inner)}_`);\n    return ph;\n  });\n\n  // Spoilers\n  text = text.replace(/\\|\\|([\\s\\S]+?)\\|\\|/g, (m, inner) => {\n    const idx = placeholders.spoilers.length;\n    const ph = `\u27ecS${idx}\u27ed`;\n    placeholders.spoilers.push(`||${escapeMarkdownV2(inner)}||`);\n    return ph;\n  });\n\n  // Escape everything else\n  text = escapeMarkdownV2(text);\n\n  // Restore placeholders\n  placeholders.links.forEach((md, i) => { text = text.replace(`\u27ecL${i}\u27ed`, md); });\n  placeholders.bolds.forEach((md, i) => { text = text.replace(`\u27ecB${i}\u27ed`, md); });\n  placeholders.italics.forEach((md, i) => { text = text.replace(`\u27ecI${i}\u27ed`, md); });\n  placeholders.spoilers.forEach((md, i) => { text = text.replace(`\u27ecS${i}\u27ed`, md); });\n\n  return text;\n}\n\n// ============ Chunking helpers ============\n/**\n * Split text into Telegram-safe chunks <= maxLen.\n * Prefers paragraph boundaries, then sentence boundaries, then words.\n * Falls back to hard cuts only when unavoidable (e.g., extremely long URL).\n */\nfunction chunkForTelegram(text, maxLen = SAFE_BUDGET) {\n  if (!text || text.length <= maxLen) return [text || ''];\n\n  const parts = [];\n  let buffer = '';\n\n  const flush = () => {\n    if (buffer) {\n      parts.push(buffer);\n      buffer = '';\n    }\n  };\n\n  // 1) Paragraph-level packing\n  const paragraphs = text.split(/\\n{2,}/);\n  for (const pRaw of paragraphs) {\n    const p = pRaw; // keep paragraph as-is\n    const candidate = buffer ? buffer + '\\n\\n' + p : p;\n    if (candidate.length <= maxLen) {\n      buffer = candidate;\n      continue;\n    }\n    if (p.length <= maxLen) {\n      flush();\n      buffer = p;\n      continue;\n    }\n\n    // 2) Sentence-level packing (paragraph is still too big)\n    flush();\n    const sentences = p.split(/(?<=[.!?\u2026])\\s+(?=[^\\s])/u);\n    let sBuf = '';\n    for (const s of sentences) {\n      const sCandidate = sBuf ? sBuf + ' ' + s : s;\n      if (sCandidate.length <= maxLen) {\n        sBuf = sCandidate;\n        continue;\n      }\n      if (s.length <= maxLen) {\n        if (sBuf) parts.push(sBuf);\n        sBuf = s;\n        continue;\n      }\n\n      // 3) Word-level packing (sentence is still too big)\n      if (sBuf) { parts.push(sBuf); sBuf = ''; }\n      let wBuf = '';\n      const words = s.split(/\\s+/);\n      for (const w of words) {\n        const wCandidate = wBuf ? wBuf + ' ' + w : w;\n        if (wCandidate.length <= maxLen) {\n          wBuf = wCandidate;\n          continue;\n        }\n        if (w.length <= maxLen) {\n          if (wBuf) parts.push(wBuf);\n          wBuf = w;\n          continue;\n        }\n        // 4) Hard split (extremely long token, e.g., massive URL)\n        if (wBuf) { parts.push(wBuf); wBuf = ''; }\n        const re = new RegExp(`.{1,${maxLen}}`, 'g');\n        const hardPieces = w.match(re) || [];\n        parts.push(...hardPieces);\n      }\n      if (wBuf) parts.push(wBuf);\n    }\n    if (sBuf) parts.push(sBuf);\n  }\n  if (buffer) parts.push(buffer);\n\n  // Final safety pass: trim chunks that might still exceed MAX_TELEGRAM\n  return parts.flatMap(part => {\n    if (part.length <= MAX_TELEGRAM) return [part];\n    const re = new RegExp(`.{1,${SAFE_BUDGET}}`, 'g');\n    return part.match(re) || [];\n  });\n}\n\n// ============ Main ============\nconst inputItems = $input.all();\nconst out = [];\n\nfor (const item of inputItems) {\n  const j = item.json || {};\n  const raw =\n    j.message ?? j.output ?? j.text ?? j.content ?? '';\n\n  const formatted = processMarkdownV2Safe(raw);\n  const chunks = chunkForTelegram(formatted, SAFE_BUDGET);\n\n  chunks.forEach((chunk, idx) => {\n    out.push({\n      json: {\n        ...j,\n        message: chunk,\n        message_part_index: idx + 1,\n        message_parts_total: chunks.length,\n      },\n      binary: item.binary,\n    });\n  });\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4dc3eac9-6640-4cd8-b07d-44254df59f48",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        1040,
        1184
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6561a9a0-2d00-424d-ba86-8852dcb7e935",
      "name": "Download Image",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2144,
        1248
      ],
      "parameters": {
        "fileId": "={{ $('Telegram Trigger').item.json.message.photo[3]?.file_id || $('Telegram Trigger').item.json.message.photo[2]?.file_id || $('Telegram Trigger').item.json.message.photo[1]?.file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "53cdd022-332a-4e7f-b2a8-405a450efbe1",
      "name": "Send a text message3",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2528,
        2176
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "MarkdownV2",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b3b521db-9919-4d51-a2f0-a8816d0f7f45",
      "name": "Google Gemini Chat Model3",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2048,
        2032
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-3-flash-preview"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7cd91bbb-b625-4c9c-ac03-06925fa6734e",
      "name": "MarkdownV1",
      "type": "n8n-nodes-base.code",
      "position": [
        2368,
        2176
      ],
      "parameters": {
        "jsCode": "/**\n * MarkdownV2-safe formatter + auto-chunker for Telegram (n8n Code node)\n * --------------------------------------------------------------------\n * - Allows: *bold*, _italic_, ||spoiler||, [label](url)\n * - Escapes everything else for Telegram MarkdownV2\n * - Validates/normalizes URLs\n * - Converts \"# Heading\" lines to bold titles\n * - Splits long messages into <= 4096-char chunks (uses a 4000-char budget)\n * - Outputs one item per chunk so the Telegram node sends all parts\n *\n * Recommended: Run this node in \"Run Once for All Items\".\n */\n\nconst MAX_TELEGRAM = 4096;\nconst SAFE_BUDGET = 4000; // small margin to avoid edge overflows\n\n// ============ MarkdownV2 helpers ============\nfunction escapeMarkdownV2(text) {\n  if (!text) return '';\n  return String(text).replace(/([\\\\_*[\\]()~`>#+\\-=|{}.!])/g, '\\\\$1');\n}\n\nfunction escapeForUrl(url) {\n  return String(url).replace(/[)\\\\]/g, '\\\\$&');\n}\n\nfunction normalizeAndValidateUrl(url) {\n  let raw = String(url || '').trim();\n  try {\n    const u = new URL(raw);\n    return u.toString();\n  } catch {}\n  // Try https:// for bare domains\n  const domainLike = /^[a-z0-9.-]+\\.[a-z]{2,}([/:?#].*)?$/i.test(raw);\n  if (domainLike) {\n    try {\n      const u2 = new URL('https://' + raw);\n      return u2.toString();\n    } catch {}\n  }\n  return null;\n}\n\nfunction normalizeHeadings(text) {\n  // Turn \"# Title\" \u2192 \"*Title*\"\n  return text.replace(/^(#{1,6})\\s+(.*)$/gm, (m, hashes, title) => `*${title.trim()}*`);\n}\n\nfunction normalizeCommonMd(text) {\n  return String(text)\n    .replace(/\\*\\*([\\s\\S]*?)\\*\\*/g, '*$1*') // **bold** \u2192 *bold*\n    .replace(/__([\\s\\S]*?)__/g, '_$1_');    // __italic__ \u2192 _italic_\n}\n\n/**\n * Convert incoming text to Telegram-safe MarkdownV2.\n */\nfunction processMarkdownV2Safe(inputText) {\n  if (!inputText) return '';\n\n  let text = normalizeCommonMd(String(inputText));\n  text = normalizeHeadings(text);\n\n  const placeholders = { links: [], bolds: [], italics: [], spoilers: [] };\n\n  // Links: keep safe via placeholders during escaping\n  text = text.replace(/\\[([^\\]\\n]+)\\]\\(([^)]+)\\)/g, (m, label, url) => {\n    const normalizedUrl = normalizeAndValidateUrl(url);\n    if (!normalizedUrl) return escapeMarkdownV2(label);\n    const idx = placeholders.links.length;\n    const ph = `\u27ecL${idx}\u27ed`;\n    const safeLabel = escapeMarkdownV2(label);\n    const safeUrl = escapeForUrl(normalizedUrl);\n    placeholders.links.push(`[${safeLabel}](${safeUrl})`);\n    return ph;\n  });\n\n  // Bold\n  text = text.replace(/\\*([\\s\\S]+?)\\*/g, (m, inner) => {\n    const idx = placeholders.bolds.length;\n    const ph = `\u27ecB${idx}\u27ed`;\n    placeholders.bolds.push(`*${escapeMarkdownV2(inner)}*`);\n    return ph;\n  });\n\n  // Italic\n  text = text.replace(/_([\\s\\S]+?)_/g, (m, inner) => {\n    const idx = placeholders.italics.length;\n    const ph = `\u27ecI${idx}\u27ed`;\n    placeholders.italics.push(`_${escapeMarkdownV2(inner)}_`);\n    return ph;\n  });\n\n  // Spoilers\n  text = text.replace(/\\|\\|([\\s\\S]+?)\\|\\|/g, (m, inner) => {\n    const idx = placeholders.spoilers.length;\n    const ph = `\u27ecS${idx}\u27ed`;\n    placeholders.spoilers.push(`||${escapeMarkdownV2(inner)}||`);\n    return ph;\n  });\n\n  // Escape everything else\n  text = escapeMarkdownV2(text);\n\n  // Restore placeholders\n  placeholders.links.forEach((md, i) => { text = text.replace(`\u27ecL${i}\u27ed`, md); });\n  placeholders.bolds.forEach((md, i) => { text = text.replace(`\u27ecB${i}\u27ed`, md); });\n  placeholders.italics.forEach((md, i) => { text = text.replace(`\u27ecI${i}\u27ed`, md); });\n  placeholders.spoilers.forEach((md, i) => { text = text.replace(`\u27ecS${i}\u27ed`, md); });\n\n  return text;\n}\n\n// ============ Chunking helpers ============\n/**\n * Split text into Telegram-safe chunks <= maxLen.\n * Prefers paragraph boundaries, then sentence boundaries, then words.\n * Falls back to hard cuts only when unavoidable (e.g., extremely long URL).\n */\nfunction chunkForTelegram(text, maxLen = SAFE_BUDGET) {\n  if (!text || text.length <= maxLen) return [text || ''];\n\n  const parts = [];\n  let buffer = '';\n\n  const flush = () => {\n    if (buffer) {\n      parts.push(buffer);\n      buffer = '';\n    }\n  };\n\n  // 1) Paragraph-level packing\n  const paragraphs = text.split(/\\n{2,}/);\n  for (const pRaw of paragraphs) {\n    const p = pRaw; // keep paragraph as-is\n    const candidate = buffer ? buffer + '\\n\\n' + p : p;\n    if (candidate.length <= maxLen) {\n      buffer = candidate;\n      continue;\n    }\n    if (p.length <= maxLen) {\n      flush();\n      buffer = p;\n      continue;\n    }\n\n    // 2) Sentence-level packing (paragraph is still too big)\n    flush();\n    const sentences = p.split(/(?<=[.!?\u2026])\\s+(?=[^\\s])/u);\n    let sBuf = '';\n    for (const s of sentences) {\n      const sCandidate = sBuf ? sBuf + ' ' + s : s;\n      if (sCandidate.length <= maxLen) {\n        sBuf = sCandidate;\n        continue;\n      }\n      if (s.length <= maxLen) {\n        if (sBuf) parts.push(sBuf);\n        sBuf = s;\n        continue;\n      }\n\n      // 3) Word-level packing (sentence is still too big)\n      if (sBuf) { parts.push(sBuf); sBuf = ''; }\n      let wBuf = '';\n      const words = s.split(/\\s+/);\n      for (const w of words) {\n        const wCandidate = wBuf ? wBuf + ' ' + w : w;\n        if (wCandidate.length <= maxLen) {\n          wBuf = wCandidate;\n          continue;\n        }\n        if (w.length <= maxLen) {\n          if (wBuf) parts.push(wBuf);\n          wBuf = w;\n          continue;\n        }\n        // 4) Hard split (extremely long token, e.g., massive URL)\n        if (wBuf) { parts.push(wBuf); wBuf = ''; }\n        const re = new RegExp(`.{1,${maxLen}}`, 'g');\n        const hardPieces = w.match(re) || [];\n        parts.push(...hardPieces);\n      }\n      if (wBuf) parts.push(wBuf);\n    }\n    if (sBuf) parts.push(sBuf);\n  }\n  if (buffer) parts.push(buffer);\n\n  // Final safety pass: trim chunks that might still exceed MAX_TELEGRAM\n  return parts.flatMap(part => {\n    if (part.length <= MAX_TELEGRAM) return [part];\n    const re = new RegExp(`.{1,${SAFE_BUDGET}}`, 'g');\n    return part.match(re) || [];\n  });\n}\n\n// ============ Main ============\nconst inputItems = $input.all();\nconst out = [];\n\nfor (const item of inputItems) {\n  const j = item.json || {};\n  const raw =\n    j.message ?? j.output ?? j.text ?? j.content ?? '';\n\n  const formatted = processMarkdownV2Safe(raw);\n  const chunks = chunkForTelegram(formatted, SAFE_BUDGET);\n\n  chunks.forEach((chunk, idx) => {\n    out.push({\n      json: {\n        ...j,\n        message: chunk,\n        message_part_index: idx + 1,\n        message_parts_total: chunks.length,\n      },\n      binary: item.binary,\n    });\n  });\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e8e8f9dc-461c-4aa5-b862-de736e74ee4d",
      "name": "Get Rizzler Profile",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        1264,
        1184
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
              "lookupColumn": "ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1156603750,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=1156603750",
          "cachedResultName": "Rizzler Profile"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "192384a8-e622-4e30-b907-30677654fb1e",
      "name": "Registered?",
      "type": "n8n-nodes-base.if",
      "position": [
        1488,
        1184
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b9e63bbf-24e6-424b-ba4e-6acd3f17b57c",
              "operator": {
                "type": "number",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.ID }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a2d8609c-529d-4646-9960-71c78ecd1e5e",
      "name": "Register Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2048,
        2176
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "options": {
          "systemMessage": "=### IDENTITY & PERSONA\nYou are **Rizz AI**, the user's chaotic but genius Wingman.\nYou are NOT a formal assistant. You are a \"Bro\" who has read every psychology book and seen every meme.\nYour vibe is: **Based, Meme-heavy, Brutally Honest, and High IQ.**\n\n**Forbidden Emojis:** \ud83d\ude80, \ud83e\udd16, \ud83d\udc4b (Too corporate).\n**Allowed Emojis:** \ud83d\udc80, \ud83e\udd21, \ud83d\uddff, \ud83e\udde2, \ud83d\udcc9, \ud83d\udea9, \ud83d\udfe2, \ud83e\udd76, \ud83e\udd75, \ud83e\udd1d, \ud83d\udeac.\n\n### MISSION\nYour goal is to set up the User's (\"The Rizzler\") profile in the database. You need 4 pieces of info to unlock the full system.\n\n### \ud83d\udd11 DATA TO COLLECT (Slot Filling)\nGather these 4 fields naturally. Do not ask for them all at once like a cop.\n\n1.  **Name:** What should I call you? (Real name or cool nickname).\n2.  **Dating_Style:** How do you text?\n    *   *If user is unsure, offer these OPTIONS:*\n        *   A) **The Comedian** \ud83e\udd21 (Funny, chaotic, self-deprecating)\n        *   B) **The Mystery** \ud83d\udd75\ufe0f\u200d\u2642\ufe0f (Short texts, slow replies, stoic)\n        *   C) **The Lover Boy** \ud83c\udf39 (Sweet, poetic, intense)\n        *   D) **The Direct** \ud83d\uddff (Bold, straight to the point, no games)\n3.  **Goals:** What are we hunting for?\n    *   *Options:* \ud83d\udc8d Long Term / \ud83e\udd42 Casual / \ud83d\udc7b Just for the plot.\n4.  **Language:** English or Spanish?\n\n### \ud83e\udde0 INTERACTION RULES\n- **Speak Gen Z:** Use terms like \"No cap\", \"Bet\", \"Down bad\", \"Rizz\", \"Red flag\", \"Main character energy\".\n- **Be Reactive:**\n    - If they pick a weird name: \"Weird flex but ok \ud83d\udc80. I'll call you [Name].\"\n    - If they pick 'Lover Boy': \"Bold choice in this economy \ud83d\udcc9. But I respect it.\"\n- **Give Options:** Always guide them if they hesitate. \"Bro, just pick one: A) Funny, B) Serious.\"\n\n### \ud83d\udee0\ufe0f TOOL USAGE (The Handoff)\nONLY when you have all 4 fields, call the tool: `create_rizzler_profile`.\n\n### \u2705 THE SEAMLESS TRANSITION (Crucial)\nOnce the tool returns \"Success\", DO NOT say \"Registration complete\".\nInstead, immediately pivot to the game. Act as if the paperwork is done and the mission starts NOW.\n\n**Final Response Template:**\n\"Aight [Name], you're locked in. \ud83d\udd12\nProfile: [Style] | Goal: [Goal].\n\nNow, let's cook. \ud83c\udf73\n**Send me a screenshot of a chat or a profile right now.**\nOr tell me, who are we texting? The ex? The gym crush? \ud83e\udd21 Give me the tea.\"\n\n### EXAMPLE CONVERSATION\n**User:** \"Hi\"\n**You:** \"Yo. Welcome to the Dojo. \ud83d\uddff Before we fix your dry texts, I need to know who I'm working with. What's your name?\"\n**User:** \"David\"\n**You:** \"Sup David. \ud83e\udd1d Alright, what's your game plan?\nA) Funny/Chaotic \ud83e\udd21\nB) Mysterious/Cool \ud83e\udd76\nC) Direct/Bold \ud83d\uddff\"\n**User:** \"B\"\n**You:** \"The strong silent type. I see you. \ud83d\udd76\ufe0f And what are we looking for? Wifey material \ud83d\udc8d or just for the weekend \ud83e\udd42?\""
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "5b6d393c-dcc4-4671-87c6-ac856b45d5bb",
      "name": "get_message (register)",
      "type": "n8n-nodes-base.set",
      "position": [
        1776,
        2176
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "801ec600-22ad-4a94-a2b4-ae72eb271df0",
              "name": "message",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.text }}"
            },
            {
              "id": "263071fb-bcdf-42b0-bb46-71b75fa0bf2a",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9bf762cf-2fb1-4887-8bc1-b02d9e0917f8",
      "name": "create_rizzler_profile",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2192,
        2384
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ $json.chat_id }}",
            "Name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Name', `he user's name or nickname (e.g., 'David', 'Big Boss').`, 'string') }}",
            "Goals": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Goals', `What the user is looking for (e.g., 'Long Term \ud83d\udc8d', 'Casual \ud83e\udd42').`, 'string') }}",
            "Language": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Language', `Preferred language (English or Spanish).`, 'string') }}",
            "Daiting_Style": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Daiting_Style', `description\": \"The selected personality vibe (e.g., 'Funny \ud83e\udd21', 'Direct \ud83d\uddff', 'Mysterious \ud83d\udd75\ufe0f\u200d\u2642\ufe0f').`, 'string') }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Daiting_Style",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Daiting_Style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Goals",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Goals",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Language",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Language",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1156603750,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=1156603750",
          "cachedResultName": "Rizzler Profile"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "50970112-2ce1-4aa9-b299-01072dc69e98",
      "name": "search_leads",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2928,
        1456
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('values0_Value', `The name to search for (e.g., 'Sarah', 'Jessica').`, 'string') }}",
              "lookupColumn": "Name"
            },
            {
              "lookupValue": "={{ $json.chat_id }}",
              "lookupColumn": "ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=0",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "earches the database for a lead by name. Returns the FULL lead object (ID, Name, Stage, Score, Profile Summary). Use this to get the current data before updating."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "1d28fd69-f708-42ba-96b3-44838f9a6c82",
      "name": "get_lead_history",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3072,
        1456
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('values0_Value', `The Leads ID from the search_leads tool`, 'string') }}",
              "lookupColumn": "Lead ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1492978715,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=1492978715",
          "cachedResultName": "Conversation Logs"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "Retrieves the past conversation logs and notes for a specific Lead ID."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "aaf798c4-e749-4210-ac58-dc1d4caa13b0",
      "name": "create_new_lead",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3216,
        1456
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ Math.random().toString(16).substring(8) }}",
            "Name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Name', `The Lead's name`, 'string') }}",
            "Stage": "=\ud83d\udce5 New Match",
            "Rizzler ID": "={{ $json.chat_id }}",
            "Last Contact": "={{ $now }}",
            "Interest Score": "=50",
            "Profile Summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Profile_Summary', `Age, Job, Bio details, Visuals.`, 'string') }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rizzler ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Rizzler ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stage",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Stage",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Interest Score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Interest Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Profile Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Next Action",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Next Action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Last Contact",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Last Contact",
              "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/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=0",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "Registers a NEW person in the database. Only use this if search_leads returns 0 results."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "ca096be1-3098-41f0-867b-e46177bf4bd3",
      "name": "update_profile",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3632,
        1456
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ $json.chat_id }}",
            "Name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Name', `User's Name. Keep existing if not changing.`, 'string') }}",
            "Goals": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Goals', `Goal: 'Long Term \ud83d\udc8d', 'Casual \ud83e\udd42', 'Just for the plot \ud83d\udc7b'.`, 'string') }}",
            "Language": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Language', `User's preferred language`, 'string') }}",
            "Daiting_Style": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Daiting_Style', `Vibe: 'The Comedian \ud83e\udd21', 'The Mystery \ud83d\udd75\ufe0f\u200d\u2642\ufe0f', 'The Lover Boy \ud83c\udf39', 'The Direct \ud83d\uddff'.`, 'string') }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Daiting_Style",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Daiting_Style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Goals",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Goals",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Language",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Language",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1156603750,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=1156603750",
          "cachedResultName": "Rizzler Profile"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "Updates the User's (Rizzler) own settings. WARNING: This performs a full row update. You MUST provide values for ALL fields. If a field (e.g., Name) is not changing, RE-SEND the existing value from the context."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c827b987-5992-41af-a5e2-b4d497bf91a2",
      "name": "update_lead",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3504,
        1456
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ID__using_to_match_', `The unique ID of the lead.`, 'string') }}",
            "Name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Name', `The Lead's Name. RE-SEND the existing name if not changing.`, 'string') }}",
            "Stage": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Stage', `The current stage of the relationship\n\"enum\": [\"\ud83d\udce5 New Match\", \"\ud83d\udde3 Discovery\", \"\ud83d\udd25 Building Rapport\", \"\ud83d\uddd3 Closing\", \"\u2744\ufe0f Cold\", \"\u2705 Won\", \"\u274c Lost\"],`, 'string') }}",
            "Rizzler ID": "={{ $json.chat_id }}",
            "Next Action": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Next_Action', `actical suggestion for next time (e.g., 'Ask for date').`, 'string') }}",
            "Last Contact": "={{ $now }}",
            "Interest Score": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Interest_Score', `Interest score 1-100. Update based on sentiment.`, 'string') }}",
            "Profile Summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Profile_Summary', `The Lead's Bio/Details. RE-SEND the existing summary if not changing.`, 'string') }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rizzler ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Rizzler ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stage",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Stage",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Interest Score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Interest Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Profile Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Next Action",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Next Action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Last Contact",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Last Contact",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=0",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "pdates the lead's status/score AND saves a summary of the interaction. WARNING: This performs a full row update. You MUST provide values for 'name' and 'profile_summary' even if they haven't changed (use the values from search_leads)."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "e15bb617-61da-4620-936d-15b0b5cf9c59",
      "name": "Input Message Router",
      "type": "n8n-nodes-base.switch",
      "position": [
        1776,
        1136
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Text",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "fcb767ee-565e-4b56-a54e-6f97f739fc24",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Voice Message",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c1016c40-f8f2-4e08-8ec8-5cdb88f5c87a",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.voice }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Image",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "f8150ac7-eea4-4658-8da9-f7a1c88a471d",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.photo[0].file_id }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "ignoreCase": false,
          "fallbackOutput": "extra",
          "allMatchingOutputs": true
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "5b34cd3b-168a-4e51-b096-2a8a1c4f8e6a",
      "name": "Simple Memory2",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        2208,
        2032
      ],
      "parameters": {
        "sessionKey": "={{ $json.chat_id }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "496bf2a8-3a7d-4fa6-bfcb-d985950743bb",
      "name": "Append Log",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        3376,
        1456
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now }}",
            "Lead ID": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Lead_ID', `The unique ID or Name of the Lead (Prospect) found in the search.`, 'string') }}",
            "Summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Summary', `A concise summary of what the User said/sent, what the Match replied, and what advice Rizz AI gave. Example: 'User sent meme, Match laughed. Rizz AI suggested asking for a date.'`, 'string') }}"
          },
          "schema": [
            {
              "id": "Lead ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Lead ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1492978715,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit#gid=1492978715",
          "cachedResultName": "Conversation Logs"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1S_ry-rSOaaaOr_xrHUHoPVeXnhtHGaBh8m7ZG6VJ7b0/edit?usp=drivesdk",
          "cachedResultName": "Rizz_CRM"
        },
        "descriptionType": "manual",
        "toolDescription": "Appends a critical summary of the conversation to the lead's permanent record."
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6d4404a0-3993-4719-b830-e5786e0a4ab5",
      "name": "Rizz AI",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3120,
        1184
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "options": {
          "systemMessage": "=### \ud83d\udc51 IDENTITY & PERSONA\nYou are **Rizz AI**, the User's Elite Dating Strategist.\n**Vibe:** Your best friend who happens to be a dating genius. Relaxed, high IQ, minimal fluff.\n**Role:** You talk naturally first, then offer tools. You are NOT a robot filling a form.\n\n### \ud83d\udcca USER CONTEXT\n- **Name:** {{ $('Get Rizzler Profile').item.json.Name }}\n- **Style:** {{ $('Get Rizzler Profile').item.json.Daiting_Style }}\n- **Goal:** {{ $('Get Rizzler Profile').item.json.Goals }}\n- **Language:** {{ $('Get Rizzler Profile').item.json.Language }} (\u26a0\ufe0f STRICTLY REPLY IN THIS LANGUAGE)\n\n### \ud83e\udde0 INTELLIGENT FLOW (The Brain)\n1.  **CONTEXT AWARENESS:**\n    *   *Did the user just ask to Register/Update?* -> Confirm the action naturally. **DO NOT** generate message options unless asked.\n    *   *Did the user send a Screenshot/Text?* -> Analyze it and provide **3 High-Value Options**.\n    *   *Is the user asking for a rev

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Rizz AI is not just a chatbot; it's a full-featured, AI-powered CRM for your dating life.

Source: https://n8n.io/workflows/13446/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This project is a template for building a complete academic virtual assistant using n8n. It connects to Telegram, answers frequently asked questions by querying MongoDB, keeps the community informed a

Telegram, MongoDB, Telegram Trigger +6
AI & RAG

Telegram Trigger receives incoming messages (text, voice, photo, document). Switch routes by message type to appropriate processors: Text → forwarded as-is. Voice → downloaded and sent to Transcribe a

Memory Buffer Window, Telegram Trigger, Telegram +12
AI & RAG

Transform your Telegram messenger into a powerful, multi-modal personal or team assistant. This n8n workflow creates an intelligent agent that can understand text, voice, images, and documents, and ta

Memory Buffer Window, Telegram Trigger, Telegram +10
AI & RAG

A comprehensive n8n workflow demonstrating advanced AI agent orchestration, stateful conversation management, and multi-modal input processing for nutrition tracking applications.

Telegram, Memory Buffer Window, Google Gemini Chat +6
AI & RAG

This workflow transforms your Telegram bot into an intelligent creative assistant. It can chat conversationally, fetch trending image prompts from PromptHero for inspiration, or perform a deep "remix"

Telegram Trigger, Output Parser Structured, Telegram +6