Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/banredco/helpdesk/llms.txt

Use this file to discover all available pages before exploring further.

Banred Helpdesk no usa webhooks ni conexiones push para recibir correos: opera con un modelo de extracción periódica (pull-based). Cada cierto tiempo, dos scripts PHP consultan activamente los buzones configurados, descargan los mensajes no leídos y los convierten en tickets nuevos o en respuestas a tickets existentes. Para que este proceso ocurra de forma continua y automática, ambos scripts deben ejecutarse como tareas programadas (cron jobs) en el servidor.

Cron job para buzones Gmail API

El script cron/process-gmail.php gestiona todos los buzones cuyo connection_type es gmail. Flujo de ejecución:
  1. Consulta la tabla mailboxes filtrando por is_active = 1 y connection_type = 'gmail'.
  2. Por cada buzón encontrado, llama a GmailMailboxProcessor::processMailbox($mailbox).
  3. Dentro de processMailbox, se invoca GmailService::getMessages($email) para obtener hasta 20 mensajes no leídos del buzón.
  4. Para cada mensaje, se verifica en la tabla email_imports si el gmail_message_id ya fue procesado; si es así, se omite.
  5. Se llama a GmailService::parseMessage() y GmailService::getAttachments() para extraer remitente, asunto, cuerpo y adjuntos.
  6. El mensaje se pasa a EmailProcessor::process($mailbox, $data) para crear el ticket o añadir la respuesta.
  7. Si el procesamiento es exitoso, se registra el gmail_message_id en email_imports con process_status = 'Procesado'.
  8. Finalmente, GmailService::markAsRead() marca el mensaje como leído en Gmail para que no sea procesado nuevamente.
  9. Cualquier excepción durante el procesamiento de un mensaje se captura y se registra con error_log(), sin interrumpir el procesamiento del resto de los mensajes.

Cron job para buzones IMAP

El script cron/process-imap.php sigue el mismo patrón para los buzones con connection_type = 'imap' y create_tickets = 1. Diferencias respecto al flujo Gmail:
  • La conexión se establece con imap_open() usando host, puerto, usuario y contraseña del buzón.
  • Los mensajes no leídos se recuperan con imap_search($connection, 'UNSEEN').
  • El UID de cada mensaje se verifica en email_imports (columna message_uid) para evitar duplicados.
  • Antes de procesar, el script inspecciona las cabeceras (imap_fetchheader) para detectar y descartar respuestas automáticas. Filtra cabeceras como auto-submitted: auto-replied, x-autoreply, precedence: bulk y x-ms-exchange-generated-message-source, entre otras.
  • También descarta mensajes cuyo asunto contenga cadenas de respuesta automática como automatic reply, out of office, fuera de oficina, respuesta automática o mail delivery failed.
  • El cuerpo se extrae navegando la estructura MIME del mensaje y se decodifica con quoted_printable_decode().
  • Los adjuntos se detectan recorriendo $structure->parts de forma recursiva con la función extractAttachments().
  • Al finalizar, el mensaje se marca como leído con imap_setflag_full($connection, $emailNumber, '\\Seen').

Configuración en el servidor

Edita el crontab del usuario del servidor web para añadir ambas tareas. Se recomienda una ejecución cada minuto para garantizar una sincronización en tiempo casi real:
crontab -e
Añade las siguientes líneas:
* * * * * php /var/www/helpdesk_ban/cron/process-gmail.php >> /var/log/helpdesk-gmail.log 2>&1
* * * * * php /var/www/helpdesk_ban/cron/process-imap.php >> /var/log/helpdesk-imap.log 2>&1
La redirección >> /var/log/helpdesk-gmail.log 2>&1 acumula la salida estándar y los errores en archivos de log separados para cada canal, facilitando la revisión de incidentes.
Ejecuta los cron jobs con el mismo usuario que el servidor web (www-data en la mayoría de configuraciones con Apache o Nginx). El proceso Gmail necesita leer el archivo google-workspace.json con las credenciales de la cuenta de servicio. Si el cron corre como root u otro usuario, es probable que encuentre errores de permisos al leer ese archivo y la sincronización fallará silenciosamente.

Lógica de EmailProcessor

Independientemente del canal de entrada (Gmail o IMAP), todos los mensajes llegan a EmailProcessor::process($mailbox, $data). Este método aplica la siguiente lógica para decidir qué hacer con cada correo: 1. Detección de respuesta a ticket existente El procesador aplica una expresión regular sobre el asunto del correo:
preg_match('/\[Ticket\s+#([0-9]+)\]/i', $subject, $ticketMatch);
Si el asunto contiene el patrón [Ticket #NNN], se extrae el número de ticket y se llama a processReply(). 2. Procesamiento de respuesta (processReply)
  • Busca en la tabla tickets el registro con ese ticket_number.
  • Inserta un nuevo registro en ticket_messages con el cuerpo del mensaje (limpiado con cleanReplyBody() para eliminar citas previas y firmas automáticas).
  • Si el ticket estaba en estado resuelto (4) o cerrado (5), lo reabre automáticamente a estado 1 y registra la actividad como 'Reabierto'.
  • Si el ticket estaba en otro estado, actualiza activity_label a 'Cliente respondió' y actualiza last_reply_at.
  • Crea una notificación interna mediante Notification::create().
3. Creación de ticket nuevo (processNewTicket) Si el asunto no contiene ningún patrón de ticket:
  • Busca al cliente por email en la tabla customers. Si no existe, lo crea automáticamente con el nombre y correo extraídos del encabezado del mensaje.
  • Calcula el siguiente ticket_number con MAX(ticket_number) + 1.
  • Inserta el ticket en la tabla tickets con status_id = 1 (Nuevo), priority_id = 2 (Media) y source = 'email'.
  • Inserta el cuerpo del correo como primer mensaje en ticket_messages.
  • Si hay adjuntos, los guarda en uploads/tickets/ e inserta los registros en ticket_attachments.
  • Envía la notificación al cliente disparando MailService::sendTemplate('ticket_created', $ticketId).
Cuando un cliente responde directamente al correo de notificación de un ticket, el asunto incluye automáticamente el patrón [Ticket #123] porque así está configurada la plantilla de respuesta. Gracias a esto, el sistema detecta la respuesta y la ensarta en el hilo del ticket correcto sin intervención manual del agente.

Sincronización manual

Si necesitas procesar los correos pendientes sin esperar al próximo ciclo del cron, los administradores pueden acceder a /admin/mail-sync.php desde el navegador para disparar la sincronización de forma inmediata. Esto es útil durante la configuración inicial, al resolver incidentes o cuando se necesita confirmar que un correo entrante fue procesado correctamente.

Build docs developers (and LLMs) love