From 9ef9194f9ee6a43fd553dd34e944ceb9c28308bd Mon Sep 17 00:00:00 2001 From: Axel Axt Date: Tue, 4 Aug 2026 19:21:38 -0300 Subject: [PATCH] Carpeta helpers --- app/helpers/chatbot_helper.php | 87 +++++++++++ app/helpers/crypto.php | 57 ++++++++ app/helpers/email_actualizar.php | 157 ++++++++++++++++++++ app/helpers/email_finalizado.php | 156 ++++++++++++++++++++ app/helpers/email_recuperar_cuenta.php | 86 +++++++++++ app/helpers/groq_helper.php | 191 +++++++++++++++++++++++++ 6 files changed, 734 insertions(+) create mode 100644 app/helpers/chatbot_helper.php create mode 100644 app/helpers/crypto.php create mode 100644 app/helpers/email_actualizar.php create mode 100644 app/helpers/email_finalizado.php create mode 100644 app/helpers/email_recuperar_cuenta.php create mode 100644 app/helpers/groq_helper.php diff --git a/app/helpers/chatbot_helper.php b/app/helpers/chatbot_helper.php new file mode 100644 index 0000000..ba28658 --- /dev/null +++ b/app/helpers/chatbot_helper.php @@ -0,0 +1,87 @@ + [ + 'cliente', + 'clientes' + ], + + 'ventas' => [ + 'venta', + 'ventas', + 'factura', + 'comprobante' + ], + + 'servicios' => [ + 'servicio', + 'servicios', + 'reparacion', + 'tecnico' + ], + + 'productos' => [ + 'producto', + 'productos', + 'stock', + 'categoria' + ], + + 'pedidos' => [ + 'pedido', + 'pedidos', + 'remito' + ], + + 'presupuestos' => [ + 'presupuesto', + 'presupuestos', + 'cotizacion' + ], + + 'proveedores' => [ + 'proveedor', + 'proveedores' + ], + + 'usuarios' => [ + 'usuario', + 'usuarios', + 'admin', + 'operador' + ], + + 'informes' => [ + 'informe', + 'informes', + 'balance', + 'grafico' + ], + + 'copias_seguridad' => [ + 'backup', + 'backups', + 'copia', + 'restaurar' + ] + ]; + + foreach ($mapa as $modulo => $palabras) { + + foreach ($palabras as $palabra) { + + if (str_contains($mensaje, $palabra)) { + return $modulo; + } + } + } + + return 'general'; +} \ No newline at end of file diff --git a/app/helpers/crypto.php b/app/helpers/crypto.php new file mode 100644 index 0000000..f85f874 --- /dev/null +++ b/app/helpers/crypto.php @@ -0,0 +1,57 @@ +obtenerDatosEnvioMail(); + + if (isset($datosConfig['contrasenia_emisor'])) { + $datosConfig['contrasenia_emisor'] = decryptData($datosConfig['contrasenia_emisor']); + } + + if ((int) $datosConfig['mail_automatico'] === 1) { + + $mail = new PHPMailer(true); + + try { + + // CONFIG SMTP + + $mail->isSMTP(); + $mail->Host = 'smtp.gmail.com'; + $mail->SMTPAuth = true; + $mail->Username = $datosConfig['email_emisor']; + $mail->Password = $datosConfig['contrasenia_emisor']; + $mail->SMTPSecure = 'tls'; + $mail->Port = 587; + + // CODIFICACIÓN (evita problemas con tildes) + + $mail->CharSet = 'UTF-8'; + + // REMITENTE + + $mail->setFrom($datosConfig['email_emisor'], 'ByteCenter'); + + // DESTINATARIO + + if (empty($cliente['email'])) { + return false; + } + + $mail->addAddress($cliente['email'], $cliente['nombre'] . ' ' . $cliente['apellido']); + + // DEFINIMOS ARRAY HTML DE INSUMOS UTILIZADOS + + $productos = ""; + + if (!empty($insumos)) { + + foreach ($insumos as $i) { + $productos .= " + + {$i['marca']} {$i['modelo']} + $ {$i['precio_unitario']} + {$i['cantidad']} + $ {$i['subtotal']} + + "; + } + } + + // POR SI LA LISTA VIENE VACIA + + if (empty($insumos)) { + $productos = " + + + - + + + "; + } + + + // SANEAMIENTO DE HORA (substraer segundos) + + $datosConfig['hora_apertura_maniana'] = substr($datosConfig['hora_apertura_maniana'], 0, 5); + $datosConfig['hora_cierre_maniana'] = substr($datosConfig['hora_cierre_maniana'], 0, 5); + $datosConfig['hora_apertura_tarde'] = substr($datosConfig['hora_apertura_tarde'], 0, 5); + $datosConfig['hora_cierre_tarde'] = substr($datosConfig['hora_cierre_tarde'], 0, 5); + + // CONTENIDO + + $numeroServicio = '0001-' . str_pad($servicio['id'], 8, '0', STR_PAD_LEFT); + + $mail->isHTML(true); + $mail->Subject = 'ByteCenter - Servicio a Realizar - N° ' . $numeroServicio; + + $mail->Body = " +

Servicio a Realizar

+ +

Cliente: ". $cliente['nombre'] . ' ' . $cliente['apellido'] ."

+

Equipo: ". $servicio['equipo'] ."

+

Problema: ". $servicio['problema']. "

+ +
+

Detalles de Servicio

+

Trabajo a Relizar

+

". $servicio['detalle_realizado']. "

+ +

Insumos a Utilizar

+ + + + + + + + + $productos +
ModeloPrecio UnitarioCantidadSubtotal
+ +

Totales

+

Mano de obra: ". "$" . number_format($servicio['mano_obra'], 2, ',', '.'). "

+

Total: ". "$" . number_format($servicio['total'], 2, ',', '.'). "

+ +
+

Esperamos su confirmación para la realización del servicio, en caso de cancelarlo podrá retirar el equipo en nuestro local ubicado en {$datosConfig['direccion']}, {$datosConfig['ciudad']} y abonar el monto por revisión y desarme: $ {$datosConfig['cargo_cancelacion_servicio']}.

+

Nuestros horarios de atención son: {$datosConfig['hora_apertura_maniana']} - {$datosConfig['hora_cierre_maniana']} y {$datosConfig['hora_apertura_tarde']} - {$datosConfig['hora_cierre_tarde']}.

+

Por cualquier duda o consulta: {$datosConfig['telefono']} - {$datosConfig['email']}.

+

Gracias por confiar en nuestro servicio técnico.

+

ByteCenter

+ "; + + // Versión texto plano (por compatibilidad) + + $mail->AltBody = "Servicio finalizado - Total: $ {$servicio['total']}"; + + $mail->send(); + + return true; + + } catch (Exception $e) { + + // Log interno + + error_log($e->getMessage()); + + return false; + } + } else { + return true; + } +} \ No newline at end of file diff --git a/app/helpers/email_finalizado.php b/app/helpers/email_finalizado.php new file mode 100644 index 0000000..e289e8d --- /dev/null +++ b/app/helpers/email_finalizado.php @@ -0,0 +1,156 @@ +obtenerDatosEnvioMail(); + + if (isset($datosConfig['contrasenia_emisor'])) { + $datosConfig['contrasenia_emisor'] = decryptData($datosConfig['contrasenia_emisor']); + } + + if ((int) $datosConfig['mail_automatico'] === 1) { + + $mail = new PHPMailer(true); + + try { + + // CONFIG SMTP + + $mail->isSMTP(); + $mail->Host = 'smtp.gmail.com'; + $mail->SMTPAuth = true; + $mail->Username = $datosConfig['email_emisor']; + $mail->Password = $datosConfig['contrasenia_emisor']; + $mail->SMTPSecure = 'tls'; + $mail->Port = 587; + + // CODIFICACIÓN (evita problemas con tildes) + + $mail->CharSet = 'UTF-8'; + + // REMITENTE + + $mail->setFrom($datosConfig['email_emisor'], 'ByteCenter'); + + // DESTINATARIO + + if (empty($cliente['email'])) { + return false; + } + + $mail->addAddress($cliente['email'], $cliente['nombre'] . ' ' . $cliente['apellido']); + + // DEFINIMOS ARRAY HTML DE INSUMOS UTILIZADOS + + $productos = ""; + + if (!empty($insumos)) { + + foreach ($insumos as $i) { + $productos .= " + + {$i['marca']} {$i['modelo']} + $ {$i['precio_unitario']} + {$i['cantidad']} + $ {$i['subtotal']} + + "; + } + } + + // POR SI LA LISTA VIENE VACIA + + if (empty($insumos)) { + $productos = " + + + - + + + "; + } + + // SANEAMIENTO DE HORA (substraer segundos) + + $datosConfig['hora_apertura_maniana'] = substr($datosConfig['hora_apertura_maniana'], 0, 5); + $datosConfig['hora_cierre_maniana'] = substr($datosConfig['hora_cierre_maniana'], 0, 5); + $datosConfig['hora_apertura_tarde'] = substr($datosConfig['hora_apertura_tarde'], 0, 5); + $datosConfig['hora_cierre_tarde'] = substr($datosConfig['hora_cierre_tarde'], 0, 5); + + // CONTENIDO + + $numeroServicio = '0001-' . str_pad($servicio['id'], 8, '0', STR_PAD_LEFT); + + $mail->isHTML(true); + $mail->Subject = 'ByteCenter - Servicio Finalizado - N°' . $numeroServicio; + + $mail->Body = " +

Servicio Finalizado

+ +

Cliente: ". $cliente['nombre'] . ' ' . $cliente['apellido'] ."

+

Equipo: ". $servicio['equipo'] ."

+

Problema: ". $servicio['problema']. "

+ +
+

Detalles de Servicio

+

Trabajo Realizado

+

". $servicio['detalle_realizado']. "

+ +

Insumos Utilizados

+ + + + + + + + + $productos +
ModeloPrecio UnitarioCantidadSubtotal
+ +

Totales

+

Mano de obra: ". "$" . number_format($servicio['mano_obra'], 2, ',', '.'). "

+

Total: ". "$" . number_format($servicio['total'], 2, ',', '.'). "

+ +
+

Cuando desee puede pasar por nuestro local para retirar su equipo, nos encontramos en {$datosConfig['direccion']}, {$datosConfig['ciudad']}.

+

Nuestros horarios de atención son: {$datosConfig['hora_apertura_maniana']} - {$datosConfig['hora_cierre_maniana']} y {$datosConfig['hora_apertura_tarde']} - {$datosConfig['hora_cierre_tarde']}.

+

Por cualquier duda o consulta: {$datosConfig['telefono']} - {$datosConfig['email']}.

+

Gracias por confiar en nuestro servicio técnico.

+

ByteCenter

+ "; + + // Versión texto plano (por compatibilidad) + + $mail->AltBody = "Servicio finalizado - Total: $ {$servicio['total']}"; + + $mail->send(); + + return true; + + } catch (Exception $e) { + + // Log interno + + error_log($e->getMessage()); + + return false; + } + } else { + return true; + } +} \ No newline at end of file diff --git a/app/helpers/email_recuperar_cuenta.php b/app/helpers/email_recuperar_cuenta.php new file mode 100644 index 0000000..76a9a99 --- /dev/null +++ b/app/helpers/email_recuperar_cuenta.php @@ -0,0 +1,86 @@ +obtenerDatosEnvioMail(); + + if (isset($datosConfig['contrasenia_emisor'])) { + $datosConfig['contrasenia_emisor'] = decryptData($datosConfig['contrasenia_emisor']); + } + + try { + + // CONFIG SMTP + + $mail->isSMTP(); + $mail->Host = 'smtp.gmail.com'; + $mail->SMTPAuth = true; + $mail->Username = $datosConfig['email_emisor']; + $mail->Password = $datosConfig['contrasenia_emisor']; + $mail->SMTPSecure = 'tls'; + $mail->Port = 587; + + // CODIFICACIÓN (evita problemas con tildes) + + $mail->CharSet = 'UTF-8'; + + // REMITENTE + + $mail->setFrom($datosConfig['email_emisor'], 'ByteCenter'); + + // DESTINATARIO + + if (empty($email)) { + return false; + } + + $mail->addAddress($email); + + $mail->isHTML(true); + $mail->Subject = 'Recuperación de contraseña'; + + $mail->Body = " +

Recuperación de contraseña

+ +

Hemos recibido una solicitud para restablecer su contraseña.

+ +

Para continuar con el proceso, utilice el siguiente código de verificación:

+ +

$codigo

+ +

Este código es válido por un período de 10 minutos.

+ +

Si usted no realizó esta solicitud, puede ignorar este mensaje de forma segura.

+ +
+ +

Atentamente,
+ Equipo de ByteCenter

+ "; + + $mail->send(); + + return true; + + } catch (Exception $e) { + + // Log interno + + error_log($e->getMessage()); + + return false; + } +} \ No newline at end of file diff --git a/app/helpers/groq_helper.php b/app/helpers/groq_helper.php new file mode 100644 index 0000000..c10a1ca --- /dev/null +++ b/app/helpers/groq_helper.php @@ -0,0 +1,191 @@ + true/false +- content -> respuesta del modelo +- error -> mensaje de error en caso de fallo +*/ + +function consultarChatbot($mensajes) { + + /* + DATA + + Arma el cuerpo JSON que será enviado a la API + + model: + - Modelo de IA a utilizar + + messages: + - Conversación completa + + temperature: + - Nivel de creatividad + - Mientras mas bajo, más preciso y menos inventa + */ + + $data = [ + 'model' => 'llama-3.1-8b-instant', + + 'messages' => $mensajes, + + 'temperature' => 0.2 + ]; + + /* + HEADERS HTTP + + Content-Type: + - Indicamos que enviamos JSON + + Authorization: + - Token Bearer con la API KEY + - Se obtiene desde el archivo .env cargado en $_ENV + */ + + $headers = [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $_ENV['GROQ_API_KEY'] + ]; + + /* + INICIALIZAR CURL + + cURL permite realizar solicitudes HTTP desde PHP + */ + + $ch = curl_init(); + + /* + CONFIGURACIÓN CURL + + CURLOPT_URL + - Endpoint oficial de Groq + + CURLOPT_RETURNTRANSFER + - Hace que la respuesta se devuelva como string + - Si no estuviera, la imprimiría directamente + + CURLOPT_POST + - Indicamos que la petición será POST + + CURLOPT_HTTPHEADER + - Enviamos headers HTTP + + CURLOPT_POSTFIELDS + - Enviamos el body JSON + */ + + curl_setopt_array($ch, [ + + CURLOPT_URL => 'https://api.groq.com/openai/v1/chat/completions', + + CURLOPT_RETURNTRANSFER => true, + + CURLOPT_POST => true, + + CURLOPT_HTTPHEADER => $headers, + + CURLOPT_POSTFIELDS => json_encode($data) + ]); + + /* + EJECUTAR CONSULTA + + - Se realiza la petición HTTP a Groq + */ + + $response = curl_exec($ch); + + /* + ERROR CURL + + Detecta errores de conexión reales + */ + + if (curl_errno($ch)) { + + $error = curl_error($ch); + + curl_close($ch); + + return [ + 'success' => false, + 'error' => $error + ]; + } + + /* + HTTP STATUS CODE + + Obtenemos el código HTTP de la respuesta + */ + + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + /* + CERRAR cURL + + Liberamos recursos + */ + + curl_close($ch); + + /* + RATE LIMIT + + Groq limita la cantidad de tokens por minuto + + 429 = demasiadas consultas o contexto muy grande + */ + + if ($httpCode === 429) { + + return [ + 'success' => false, + 'error' => 'Límite temporal alcanzado. Esperá unos segundos.' + ]; + } + + /* + OTROS ERRORES HTTP + + Si la respuesta no fue 200, devolvemos error (200 -> OK) + */ + + if ($httpCode !== 200) { + + return [ + 'success' => false, + 'error' => "HTTP ERROR {$httpCode}: {$response}" + ]; + } + + /* + DECODIFICAR JSON + + Convertimos la respuesta JSON de Groq en array PHP + */ + + $resultado = json_decode($response, true); + + /* + RESPUESTA FINAL + + choices[0]['message']['content'] + + Es donde Groq devuelve el texto generado + */ + + return [ + 'success' => true, + + 'content' => $resultado['choices'][0]['message']['content'] ?? 'Sin respuesta.' + ]; +} \ No newline at end of file