feat(soporte): leer el buzón por IMAP, no solo esperar el webhook
Los clientes escriben a soporte@ desde su correo de siempre. Hasta ahora eso solo llegaba si un proveedor (SendGrid/Mailgun) nos hacía POST; si nadie lo configuraba, los correos quedaban sin leer en el buzón. Ahora el cron entra al buzón cada 2 minutos, baja los no leídos, abre ticket (o los engancha al hilo si son respuesta) y los marca como leídos. La lógica de ingesta se movió a services para que webhook e IMAP se comporten igual. La contraseña del buzón se guarda cifrada (AES-GCM con APP_KEY) y no vuelve al navegador. De paso, dos bugs que impedían guardar la configuración: el formulario mandaba id=0 (gorm.Model serializa "ID"), así que cada guardado creaba una fila nueva en vez de editar la que se usa; y Updates con struct ignoraba los booleanos en false, así que desactivar algo no tenía efecto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1ebae791f6
commit
8c3ab79e88
@@ -0,0 +1,56 @@
|
||||
package services
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParsearCorreoMultipartQuotedPrintable(t *testing.T) {
|
||||
crudo := "From: =?UTF-8?Q?Juan_P=C3=A9rez?= <juan@cliente.com>\r\n" +
|
||||
"Subject: =?UTF-8?Q?No_puedo_entrar_a_la_plataforma?=\r\n" +
|
||||
"Message-Id: <abc123@cliente.com>\r\n" +
|
||||
"Content-Type: multipart/alternative; boundary=\"XX\"\r\n" +
|
||||
"\r\n" +
|
||||
"--XX\r\n" +
|
||||
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||
"\r\n" +
|
||||
"Hola, la contrase=C3=B1a no me sirve.\r\n" +
|
||||
"\r\n" +
|
||||
"El 3 de marzo, Soporte escribi=C3=B3:\r\n" +
|
||||
"> proba de nuevo\r\n" +
|
||||
"--XX\r\n" +
|
||||
"Content-Type: text/html; charset=UTF-8\r\n" +
|
||||
"\r\n" +
|
||||
"<p>ignorame</p>\r\n" +
|
||||
"--XX--\r\n"
|
||||
|
||||
c, err := parsearCorreo([]byte(crudo))
|
||||
if err != nil {
|
||||
t.Fatalf("parsearCorreo: %v", err)
|
||||
}
|
||||
if c.FromName != "Juan Pérez" {
|
||||
t.Errorf("FromName = %q, want %q", c.FromName, "Juan Pérez")
|
||||
}
|
||||
if ExtraerEmail(c.From) != "juan@cliente.com" {
|
||||
t.Errorf("From = %q", c.From)
|
||||
}
|
||||
if c.Subject != "No puedo entrar a la plataforma" {
|
||||
t.Errorf("Subject = %q", c.Subject)
|
||||
}
|
||||
if c.MessageID != "<abc123@cliente.com>" {
|
||||
t.Errorf("MessageID = %q", c.MessageID)
|
||||
}
|
||||
// Cuerpo decodificado, sin la cita del hilo anterior ni el HTML.
|
||||
if c.Texto != "Hola, la contraseña no me sirve." {
|
||||
t.Errorf("Texto = %q", c.Texto)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsearCorreoPlano(t *testing.T) {
|
||||
crudo := "From: ana@cliente.com\r\nSubject: Consulta\r\n\r\nHola\r\n"
|
||||
c, err := parsearCorreo([]byte(crudo))
|
||||
if err != nil {
|
||||
t.Fatalf("parsearCorreo: %v", err)
|
||||
}
|
||||
if c.Texto != "Hola" || c.Subject != "Consulta" {
|
||||
t.Errorf("got %+v", c)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user