foodsnap/src/n8n-coach-whatsapp.json

572 lines
20 KiB
JSON
Raw Normal View History

{
"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
}
]
]
}
}
}