fix(whatsapp): update Coach mode to 1 photo, fix google login infinite loading, improve welcome messages
This commit is contained in:
parent
f7753cfeb1
commit
6640162d4d
2 changed files with 43 additions and 85 deletions
|
|
@ -120,7 +120,10 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||
if (error) {
|
||||
console.error("UserContext: Falha na sessão inicial", error);
|
||||
if (mounted) setLoading(false);
|
||||
} else if (!session) {
|
||||
} else if (session) {
|
||||
// Handled gracefully: forces profile fetch if session exists but events failed
|
||||
handleSession(session);
|
||||
} else {
|
||||
// Se não há sessão e onAuthStateChange não disparou INITIAL_SESSION
|
||||
if (mounted) setLoading(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -584,6 +584,11 @@ function formatWhatsAppResponse(analysis: any): string {
|
|||
lines.push(`💡 _DICA:_ ${analysis.tip.text}`);
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
lines.push("━━━━━━━━━━━━━━━━━━━━━");
|
||||
lines.push("✨ *Gostou do visual?* Veja este e todos os seus pratos com gráficos interativos no nosso App:");
|
||||
lines.push("💻 https://foodsnap.com.br/dashboard");
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
|
|
@ -718,11 +723,14 @@ async function processMetaMessage(msg: any) {
|
|||
const userId = user.id;
|
||||
|
||||
// ── 4. Estado da conversa (Coach state machine) ─────────────
|
||||
let { data: conv } = await supabase
|
||||
let { data: convList } = await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.select("*")
|
||||
.eq("phone_number", senderNumber)
|
||||
.maybeSingle();
|
||||
.order("updated_at", { ascending: false })
|
||||
.limit(1);
|
||||
|
||||
let conv = convList && convList.length > 0 ? convList[0] : null;
|
||||
|
||||
// ── 3.5 Prevenir Mensagens Duplicadas (Retries da Meta) ──────
|
||||
if (conv && conv.last_msg_id === messageId) {
|
||||
|
|
@ -868,7 +876,7 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
// Mensagem 1: Introdução calorosa
|
||||
await sendWhatsAppMessage(
|
||||
remoteJid,
|
||||
"🤖 *Coach IA ativado!*\n\nVou analisar seu biotipo e montar um protocolo *100% personalizado* de treino e dieta.\n\nO processo é simples: você envia *3 fotos* (frente, lado e costas) e a IA retorna seu plano completo em PDF. ⚡"
|
||||
"🤖 *Coach IA Ativado!*\n\nVou analisar sua composição corporal e montar um protocolo *Titan 100% personalizado* de treino e dieta.\n\nO processo é super simples: você envia apenas *1 foto realista* e a IA processa o resto! ⚡"
|
||||
);
|
||||
|
||||
// Pequena pausa para não parecer robótico
|
||||
|
|
@ -877,7 +885,7 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
// Mensagem 2: Instrução da primeira foto
|
||||
await sendWhatsAppMessage(
|
||||
remoteJid,
|
||||
"📸 *FOTO 1 de 3 — FRENTE*\n\nEnvie uma foto de frente do seu corpo agora.\n\n✅ Boa iluminação\n✅ Roupa justa ou sem camisa\n✅ Foto do pescoço até os joelhos"
|
||||
"📸 *ENVIE SUA FOTO*\n\nTire uma selfie no espelho ou foto de frente do seu corpo.\n\n✅ Boa iluminação\n✅ Sem camisa (homens) ou de Top/Regata (mulheres)\n✅ Mostrando do pescoço até a cintura/joelhos"
|
||||
);
|
||||
}, 0);
|
||||
return new Response("Coach Started", { status: 200 });
|
||||
|
|
@ -892,7 +900,7 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
return new Response("Waiting for image", { status: 200 });
|
||||
}
|
||||
|
||||
// Offload long-running task to background
|
||||
// Offload long-running task to background
|
||||
setTimeout(async () => {
|
||||
const base64 = await getWhatsAppMedia(messageId);
|
||||
if (!base64) {
|
||||
|
|
@ -907,84 +915,20 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
|
||||
await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.update({ state: "COACH_SIDE", temp_data: { ...conv!.temp_data, front_image: fileName } })
|
||||
.eq("phone_number", senderNumber);
|
||||
|
||||
await sendWhatsAppMessage(remoteJid, "✅ Foto de frente recebida!\n\n📸 *FOTO 2 de 3 — LATERAL*\n\nAgora envie uma foto de perfil (lado direito ou esquerdo).\n\n✅ Braço ao longo do corpo\n✅ Mesmo padrão de roupa e iluminação");
|
||||
}, 0);
|
||||
return new Response("Coach Front image received", { status: 200 });
|
||||
}
|
||||
|
||||
// COACH_SIDE
|
||||
if (state === "COACH_SIDE") {
|
||||
if (!isImage) {
|
||||
setTimeout(async () => {
|
||||
await sendWhatsAppMessage(remoteJid, "⚠️ Por favor, envie a foto de *LADO*.");
|
||||
}, 0);
|
||||
return new Response("Waiting for image", { status: 200 });
|
||||
}
|
||||
|
||||
// Offload long-running task to background
|
||||
setTimeout(async () => {
|
||||
const base64 = await getWhatsAppMedia(messageId);
|
||||
if (!base64) {
|
||||
await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = `${userId}_side_${Date.now()}.jpg`;
|
||||
await supabase.storage
|
||||
.from("coach-uploads")
|
||||
.upload(fileName, base64ToUint8Array(base64), { contentType: "image/jpeg" });
|
||||
|
||||
await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.update({ state: "COACH_BACK", temp_data: { ...conv!.temp_data, side_image: fileName } })
|
||||
.eq("phone_number", senderNumber);
|
||||
|
||||
await sendWhatsAppMessage(remoteJid, "✅ Foto lateral recebida!\n\n📸 *FOTO 3 de 3 — COSTAS*\n\nEnvie agora uma foto de costas.\n\n✅ Postura ereta\n✅ Braços ao lado do corpo\n✅ Mesma roupa e iluminação");
|
||||
}, 0);
|
||||
return new Response("Coach Side image received", { status: 200 });
|
||||
}
|
||||
|
||||
// COACH_BACK
|
||||
if (state === "COACH_BACK") {
|
||||
if (!isImage) {
|
||||
setTimeout(async () => {
|
||||
await sendWhatsAppMessage(remoteJid, "⚠️ Por favor, envie a foto de *COSTAS*.");
|
||||
}, 0);
|
||||
return new Response("Waiting for image", { status: 200 });
|
||||
}
|
||||
|
||||
// Offload long-running task to background
|
||||
setTimeout(async () => {
|
||||
const base64 = await getWhatsAppMedia(messageId);
|
||||
if (!base64) {
|
||||
await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = `${userId}_back_${Date.now()}.jpg`;
|
||||
await supabase.storage
|
||||
.from("coach-uploads")
|
||||
.upload(fileName, base64ToUint8Array(base64), { contentType: "image/jpeg" });
|
||||
|
||||
await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.update({ state: "COACH_GOAL", temp_data: { ...conv!.temp_data, back_image: fileName } })
|
||||
.update({ state: "COACH_GOAL", temp_data: { ...conv!.temp_data, front_image: fileName } })
|
||||
.eq("phone_number", senderNumber);
|
||||
|
||||
await sendWhatsAppInteractiveButtons(
|
||||
remoteJid,
|
||||
"📸 Todas as fotos recebidas!\n\nAgora escolha o seu principal objetivo para o protocolo:",
|
||||
"📸 Imagem processada com sucesso!\n\nAgora para eu calibrar seu treino e dieta, *qual o seu objetivo principal?*",
|
||||
[
|
||||
{ id: "goal_hipertrofia", title: "💪 Hipertrofia" },
|
||||
{ id: "goal_hipertrofia", title: "💪 Hipertrofia (Massa)" },
|
||||
{ id: "goal_emagrecer", title: "🔥 Emagrecimento" },
|
||||
{ id: "goal_definicao", title: "📐 Definição" }
|
||||
]
|
||||
);
|
||||
}, 0);
|
||||
return new Response("Coach Back image received", { status: 200 });
|
||||
return new Response("Coach Photo image received", { status: 200 });
|
||||
}
|
||||
|
||||
// COACH_GOAL
|
||||
|
|
@ -1014,9 +958,9 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
);
|
||||
|
||||
try {
|
||||
const { front_image, side_image, back_image } = conv!.temp_data;
|
||||
const images = [front_image, side_image, back_image];
|
||||
const parts: any[] = [{ text: COACH_SYSTEM_PROMPT }, { text: `Objetivo: ${goal}` }];
|
||||
const { front_image } = conv!.temp_data;
|
||||
const images = [front_image];
|
||||
const parts: any[] = [{ text: COACH_SYSTEM_PROMPT }, { text: `Objetivo: ${goal}\n(Lembre-se: baseie-se apenas nesta imagem unica enviada pelo usuario.` }];
|
||||
|
||||
for (const imgPath of images) {
|
||||
if (imgPath) {
|
||||
|
|
@ -1212,20 +1156,20 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
|
||||
await sendWhatsAppListMenu(
|
||||
remoteJid,
|
||||
"FoodSnap IA",
|
||||
"👋 Olá! Sou sua Inteligência Artificial.\n\n📸 *Para calcular suas calorias, basta me enviar a foto do seu prato agora mesmo!*",
|
||||
"Mais Opções 📋",
|
||||
"FoodSnap IA e Nutrição",
|
||||
"Fala aí! 👋 Sou a FoodSnap, a Inteligência Artificial projetada para revolucionar seu físico.\n\nEscolha uma opção no menu abaixo para navegarmos, ou se preferir, *apenas me mande diretamente a foto do que você está comendo* e eu calculo tudo na hora!",
|
||||
"Menu Principal 👇",
|
||||
[
|
||||
{
|
||||
title: "Escanear Prato 📸",
|
||||
title: "🍽️ Scanners Diários",
|
||||
rows: [
|
||||
{ id: "action_help_photo", title: "Dicas de Fotografia", description: "Veja como tirar a foto perfeita" },
|
||||
{ id: "action_help_photo", title: "Dicas de Leitura de Prato", description: "Veja as recomendações pro bot não falhar" },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Evolução PRO 📈",
|
||||
title: "🏋️ Especialistas",
|
||||
rows: [
|
||||
{ id: "action_coach", title: "Protocólo Coach IA", description: "Gerar plano de treino pelo biotipo" },
|
||||
{ id: "action_coach", title: "Protocolo Coach de Saúde", description: "Gerar plano de treino 100% individual" },
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -1400,9 +1344,20 @@ RETORNE estritamente 3 bullet points recomendando o que o paciente pode adiciona
|
|||
);
|
||||
|
||||
// Atualizar last_analysis no temp_data para a re-pergunta "O que comer mais?"
|
||||
// Sem sobrescrever o estado caso o usuário já tenha iniciado o fluxo COACH_FRONT no meio tempo
|
||||
const { data: currentConv } = await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.select("state, temp_data")
|
||||
.eq("phone_number", senderNumber)
|
||||
.order("updated_at", { ascending: false })
|
||||
.limit(1);
|
||||
|
||||
const currentState = currentConv?.[0]?.state || "IDLE";
|
||||
const currentTempData = currentConv?.[0]?.temp_data || {};
|
||||
|
||||
await supabase
|
||||
.from("whatsapp_conversations")
|
||||
.update({ state: "IDLE", temp_data: { last_analysis: rawResponseText } })
|
||||
.update({ state: currentState, temp_data: { ...currentTempData, last_analysis: rawResponseText } })
|
||||
.eq("phone_number", senderNumber);
|
||||
|
||||
// 6g. Mapear confidence para enum do banco
|
||||
|
|
|
|||
Loading…
Reference in a new issue