diff --git a/scripts/chat-bot.ts b/scripts/chat-bot.ts new file mode 100644 index 0000000..074d7ea --- /dev/null +++ b/scripts/chat-bot.ts @@ -0,0 +1,52 @@ +/** + * Chatear con el bot de WhatsApp desde la consola, sin Meta. + * + * Habla directo con `procesarMensaje`, que es lo mismo que invoca el webhook, así + * que ejercita la conversación completa contra la base a la que apunte DATABASE_URL + * (turnos incluidos: lo que reserves acá queda guardado). + * + * DATABASE_URL=... npx tsx scripts/chat-bot.ts [telefono] + * + * Acepta entrada interactiva o por tubería (una línea por mensaje). + * Comandos propios: /salir, /reset (borra la conversación de ese teléfono). + */ + +import { createInterface } from "readline"; +import { prisma } from "../src/lib/prisma"; +import { procesarMensaje } from "../src/lib/whatsapp-bot"; + +const telefono = process.argv[2] ?? "5493510000000"; + +function mostrar(resp: { tipo: string; texto: string; botones?: { titulo: string }[] }[]) { + for (const r of resp) { + console.log("\n🤖 " + r.texto.replace(/\n/g, "\n ")); + if (r.tipo === "botones" && r.botones) + console.log(" [ " + r.botones.map((b) => b.titulo).join(" ] [ ") + " ]"); + } +} + +async function main() { + console.log(`Chateando como ${telefono}. /salir para terminar, /reset para empezar de cero.`); + + const rl = createInterface({ input: process.stdin, terminal: false }); + + for await (const linea of rl) { + const texto = linea.trim(); + if (texto === "/salir") break; + if (texto === "/reset") { + await prisma.conversacionWhatsapp.deleteMany({ where: { telefono } }); + console.log("\n(conversación borrada)"); + continue; + } + console.log(`\n👤 ${texto}`); + mostrar(await procesarMensaje(telefono, texto)); + } + + await prisma.$disconnect(); +} + +main().catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); +}); diff --git a/src/app/api/cliente/registro/route.ts b/src/app/api/cliente/registro/route.ts index 6d3ad37..be89da5 100644 --- a/src/app/api/cliente/registro/route.ts +++ b/src/app/api/cliente/registro/route.ts @@ -1,5 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; +import { aFormatoWhatsapp, esLocalArgentinoValido, normalizarTelefono } from "@/lib/telefono"; import nodemailer from "nodemailer"; const transporter = nodemailer.createTransport({ @@ -18,11 +19,29 @@ export async function POST(req: NextRequest) { if (!email || !nombre || !apellido || !telefono) return NextResponse.json({ error: "Todos los campos son requeridos" }, { status: 400 }); + if (!esLocalArgentinoValido(telefono)) + return NextResponse.json( + { error: "El teléfono debe tener 10 dígitos, sin el 0 de la característica ni el 15" }, + { status: 400 } + ); + const existe = await prisma.cliente.findUnique({ where: { email: email.toLowerCase() } }); if (existe) return NextResponse.json({ error: "El email ya está registrado" }, { status: 409 }); + // `telefonoNorm` queda igual al `wa_id` que manda Meta, que es como lo busca el bot. + const telefonoNorm = aFormatoWhatsapp(telefono); + const yaUsado = await prisma.cliente.findFirst({ where: { telefonoNorm } }); + if (yaUsado) + return NextResponse.json({ error: "Ese teléfono ya está registrado" }, { status: 409 }); + const cliente = await prisma.cliente.create({ - data: { email: email.toLowerCase().trim(), nombre, apellido, telefono }, + data: { + email: email.toLowerCase().trim(), + nombre, + apellido, + telefono: normalizarTelefono(telefono), + telefonoNorm, + }, }); await transporter.sendMail({ diff --git a/src/app/cliente/registro/page.tsx b/src/app/cliente/registro/page.tsx index 83db641..e694dad 100644 --- a/src/app/cliente/registro/page.tsx +++ b/src/app/cliente/registro/page.tsx @@ -5,19 +5,36 @@ import Link from "next/link"; import Button from "@/components/ui/Button"; import Input from "@/components/ui/Input"; import Card from "@/components/ui/Card"; +import { esLocalArgentinoValido } from "@/lib/telefono"; export default function RegistroPage() { const router = useRouter(); const [form, setForm] = useState({ email: "", nombre: "", apellido: "", telefono: "" }); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); + const [telefonoError, setTelefonoError] = useState(""); function set(field: string) { return (e: React.ChangeEvent) => setForm((f) => ({ ...f, [field]: e.target.value })); } + // El teléfono se guarda como lo espera WhatsApp: solo característica + abonado, + // sin el 0 ni el 15 (el +54 9 lo pone el prefijo fijo del campo). + function handleTelefono(e: React.ChangeEvent) { + const soloDigitos = e.target.value.replace(/\D/g, "").slice(0, 10); + setForm((f) => ({ ...f, telefono: soloDigitos })); + if (soloDigitos === "") setTelefonoError(""); + else if (soloDigitos.startsWith("0")) setTelefonoError("Sin el 0 de la característica"); + else if (soloDigitos.length < 10) setTelefonoError("Faltan dígitos (son 10 en total)"); + else setTelefonoError(""); + } + async function handleSubmit(e: React.FormEvent) { e.preventDefault(); + if (!esLocalArgentinoValido(form.telefono)) { + setTelefonoError("Revisá el número: son 10 dígitos, sin el 0 y sin el 15"); + return; + } setLoading(true); setError(""); const res = await fetch("/api/cliente/registro", { @@ -47,7 +64,20 @@ export default function RegistroPage() { - + {error &&

{error}

}