foodsnap/src/n8n-coach-whatsapp.json

572 lines
No EOL
20 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"name": "FoodSnap - Coach AI (WhatsApp)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "wa/coach-inbound",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-500,
-940
],
"id": "webhook-coach",
"name": "Webhook (Whatsapp)"
},
{
"parameters": {
"jsCode": "const body = $json.body ?? $json;\nconst data = body.data ?? {};\n\nconst remoteJid = data?.key?.remoteJid?.includes('@s.whatsapp.net') ? data.key.remoteJid : data?.key?.remoteJidAlt || '';\nconst number = remoteJid.replace(/\\D/g, '');\nconst message_id = data?.key?.id || '';\nconst text = data?.message?.conversation || data?.message?.extendedTextMessage?.text || '';\n\n// Check for image\nconst imageMessage = data?.message?.imageMessage || data?.message?.extendedTextMessage?.contextInfo?.quotedMessage?.imageMessage || null;\n\nreturn [{\n number,\n remoteJid,\n message_id,\n text,\n hasImage: !!imageMessage,\n imageMessage,\n username: data?.pushName || 'Atleta'\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-280,
-940
],
"id": "normalize-inbound",
"name": "Normalizar Dados"
},
{
"parameters": {
"operation": "executeQuery",
"query": "select * from check_access_by_whatsapp('{{ $json.number }}')",
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
-60,
-940
],
"id": "validate-user",
"name": "Validar Usuario (RPC)",
"credentials": {
"postgres": {
"id": "2JDD2OJz4cAsWb0J",
"name": "foodsnap_supabase"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "check-user",
"leftValue": "={{ $json.exists }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
160,
-940
],
"id": "if-exists",
"name": "Usuario Existe?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "check-process",
"leftValue": "={{ $json.can_process }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
400,
-1040
],
"id": "if-quota",
"name": "Tem Quota/Plano?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "check-image",
"leftValue": "={{ $node[\"Normalizar Dados\"].json.hasImage }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
640,
-940
],
"id": "if-image",
"name": "Tem Imagem?"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "FoodSnap",
"remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
"messageText": "💪 *Coach AI*: Olá! Envie uma foto do seu corpo (preferencialmente de frente, roupa de treino) para eu fazer uma análise rápida do seu biótipo e sugestão de treino.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
860,
-840
],
"id": "msg-intro",
"name": "Msg Intro",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "FoodSnap",
"remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
"messageText": "🧐 Analisando seu físico... Um momento!",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
860,
-1040
],
"id": "msg-ack",
"name": "Msg Ack",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
},
{
"parameters": {
"resource": "chat-api",
"operation": "get-media-base64",
"instanceName": "FoodSnap",
"messageId": "={{ $('Normalizar Dados').item.json.message_id }}"
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1080,
-1040
],
"id": "get-image",
"name": "Baixar Imagem",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
},
{
"parameters": {
"operation": "toBinary",
"sourceProperty": "data.base64",
"options": {}
},
"type": "n8n-nodes-base.convertToFile",
"typeVersion": 1.1,
"position": [
1300,
-1040
],
"id": "convert-binary",
"name": "Converter Binario"
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-pro-vision",
"mode": "list",
"cachedResultName": "models/gemini-pro-vision"
},
"text": "=Você é um Treinador Físico de Elite e Nutricionista Esportivo.\nAnalise a imagem fornecida (foto de corpo inteiro/físico).\n\n1. Verifique se é uma foto de corpo humano válida para análise fitness. Se não, retorne \"valid_body\": false.\n2. Se for válida, estime:\n - Biótipo Predominante (Ectomorfo, Mesomorfo, Endomorfo)\n - Percentual de Gordura (BF% aproximado)\n - Nível de Massa Muscular (Baixo, Médio, Alto)\n3. Sugira:\n - Objetivo Principal (Cutting, Bulking, Manutenção)\n - Divisão de Treino Recomendada (ex: ABC, ABCDE, Upper/Lower)\n - Foco Dietético (ex: Carb Cycling, Alto Carbo, Keto)\n - Dica de Ouro (uma frase curta de impacto)\n\nResponda APENAS em JSON estrito (sem markdown):\n{\n \"valid_body\": true,\n \"biotype\": \"...\",\n \"estimated_body_fat\": 15,\n \"muscle_mass\": \"Médio\",\n \"goal\": \"Bulking\",\n \"workout\": \"...\",\n \"diet\": \"...\",\n \"tip\": \"...\"\n}",
"inputType": "binary",
"simplify": false,
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1,
"position": [
1520,
-1100
],
"id": "analyze-gemini",
"name": "Gemini Coach Analysis",
"credentials": {
"googlePalmApi": {
"id": "T2uIVBcjJ9h8BFCC",
"name": "Backup APIKEY"
}
}
},
{
"parameters": {
"jsCode": "const raw = $json?.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\nconst clean = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nlet data = {};\ntry { \n data = JSON.parse(clean); \n} catch(e) {\n data = { valid_body: false, error: 'Failed to parse AI' };\n}\n\nreturn [{ ...data }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1740,
-1100
],
"id": "parse-response",
"name": "Parse AI JSON"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "check-validity",
"leftValue": "={{ $json.valid_body }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1960,
-1100
],
"id": "if-valid-body",
"name": "Corpo Valido?"
},
{
"parameters": {
"operation": "executeQuery",
"query": "insert into public.coach_analyses\n(\n user_id,\n source,\n image_url,\n ai_raw_response,\n ai_structured,\n biotype,\n estimated_body_fat,\n goal_suggestion,\n muscle_mass_level,\n used_free_quota\n)\nvalues\n(\n cast('{{ $(\"Validar Usuario & Quota\").item.json.user_id }}' as uuid),\n 'whatsapp',\n null,\n '{{ JSON.stringify($json) }}',\n '{{ JSON.stringify($json) }}',\n '{{ $json.biotype }}',\n cast({{ $json.estimated_body_fat || 0 }} as numeric),\n '{{ $json.goal }}',\n '{{ $json.muscle_mass }}',\n CASE\n WHEN {{ $(\"Validar Usuario & Quota\").item.json.plan_active }} = true THEN false\n ELSE true\n END\n)\nreturning id as analysis_id;",
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
2200,
-1180
],
"id": "save-db",
"name": "Salvar DB",
"credentials": {
"postgres": {
"id": "2JDD2OJz4cAsWb0J",
"name": "foodsnap_supabase"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "FoodSnap",
"remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
"messageText": "=⚡ *Análise Coach AI*\n\n🧬 *Biótipo*: {{$json.biotype}}\n⚖ *Gordura (BF)*: ~{{$json.estimated_body_fat}}%\n💪 *Massa Muscular*: {{$json.muscle_mass}}\n\n🎯 *Objetivo Sugerido*: {{$json.goal}}\n🏋 *Treino*: {{$json.workout}}\n🥗 *Dieta*: {{$json.diet}}\n\n💡 *Dica*: {{$json.tip}}\n\n_Para ver o plano completo, acesse o App!_",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
2420,
-1180
],
"id": "reply-success",
"name": "Responder Resultado",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "FoodSnap",
"remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
"messageText": "⚠️ Não consegui identificar um físico claro nesta foto. Tente enviar uma foto de corpo inteiro ou tronco, com boa iluminação.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
2200,
-980
],
"id": "reply-invalid",
"name": "Responder Invalido",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "FoodSnap",
"remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
"messageText": "🚫 *Limite do Coach Atingido*\n\nVocê já usou suas 3 análises de Coach gratuitas. Assine o plano PRO para avaliações ilimitadas! 🚀",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
640,
-1140
],
"id": "reply-limit",
"name": "Msg Limite",
"credentials": {
"evolutionApi": {
"id": "nGWBERcZoQWgdxgk",
"name": "FoodSnap"
}
}
}
],
"connections": {
"Webhook (Whatsapp)": {
"main": [
[
{
"node": "Normalizar Dados",
"type": "main",
"index": 0
}
]
]
},
"Normalizar Dados": {
"main": [
[
{
"node": "Validar Usuario & Quota",
"type": "main",
"index": 0
}
]
]
},
"Validar Usuario & Quota": {
"main": [
[
{
"node": "Usuario Existe?",
"type": "main",
"index": 0
}
]
]
},
"Usuario Existe?": {
"main": [
[
{
"node": "Tem Quota/Plano?",
"type": "main",
"index": 0
}
]
]
},
"Tem Quota/Plano?": {
"main": [
[
{
"node": "Tem Imagem?",
"type": "main",
"index": 0
}
],
[
{
"node": "Msg Limite",
"type": "main",
"index": 0
}
]
]
},
"Tem Imagem?": {
"main": [
[
{
"node": "Msg Ack",
"type": "main",
"index": 0
}
],
[
{
"node": "Msg Intro",
"type": "main",
"index": 0
}
]
]
},
"Msg Ack": {
"main": [
[
{
"node": "Baixar Imagem",
"type": "main",
"index": 0
}
]
]
},
"Baixar Imagem": {
"main": [
[
{
"node": "Converter Binario",
"type": "main",
"index": 0
}
]
]
},
"Converter Binario": {
"main": [
[
{
"node": "Gemini Coach Analysis",
"type": "main",
"index": 0
}
]
]
},
"Gemini Coach Analysis": {
"main": [
[
{
"node": "Parse AI JSON",
"type": "main",
"index": 0
}
]
]
},
"Parse AI JSON": {
"main": [
[
{
"node": "Corpo Valido?",
"type": "main",
"index": 0
}
]
]
},
"Corpo Valido?": {
"main": [
[
{
"node": "Salvar DB",
"type": "main",
"index": 0
}
],
[
{
"node": "Responder Invalido",
"type": "main",
"index": 0
}
]
]
},
"Salvar DB": {
"main": [
[
{
"node": "Responder Resultado",
"type": "main",
"index": 0
}
]
]
}
}
}