Crear la cita vía WhatsApp
¡Has llegado lejos! Desde la instalación de nuestro sistema de citas hasta conectarlo con la base de datos y habilitar las respuestas automáticas con ChatGPT, has recorrido un camino significativo en este curso. Luego, en nuestro último tutorial, exploramos cómo responder a los clientes a través de WhatsApp, dándole a nuestro sistema la capacidad de comunicarse en tiempo real en una plataforma popular y accesible. Ahora es el momento de unir todos estos componentes en nuestro tutorial final.
En esta última entrega de nuestro curso, titulada “Crear la cita vía WhatsApp”, vamos a culminar todo el proceso, dando a nuestro sistema la habilidad para no solo recibir y responder mensajes, sino también para concretar y agendar citas a través de WhatsApp. Esto implica la convergencia de todas las habilidades y conocimientos que has adquirido hasta ahora.
Estos son los puntos clave que abordaremos en este tutorial:
- Explicaremos cómo integrar todo lo que hemos construido hasta ahora para hacer que nuestro sistema de citas sea completamente funcional a través de WhatsApp.
- Analizaremos la lógica de programación necesaria para procesar la información del cliente y los mensajes entrantes para agendar citas.
- Te guiaremos a través de pruebas y escenarios en vivo para asegurarnos de que el sistema funciona de manera eficiente y sin problemas.
Al final de este tutorial, tendrás en tus manos un sistema de citas robusto, intuitivo y de vanguardia que funcionará a través de WhatsApp, proporcionando una plataforma familiar y accesible para tus clientes. Esta implementación mejorará la eficiencia de tu servicio, aumentará la satisfacción del cliente y te posicionará a la vanguardia en tu sector.
Nos entusiasma compartir esta última etapa contigo. ¡Vamos a dar este último paso juntos y hacer que tu sistema de citas sea todo un éxito! ¡Vamos a ello!
Configurar horario
En chatgpt.php agregamos este código
$fechaCita=creaCita($cliente,$telefono, $edad, $especialidad, $sintomas);
De tal suerte que nuestro codigo quedaria asi:
<?php function preguntaChatgpt($system, $pregunta, $telefonoCliente,$listaCategorias){ require "config.php"; $config = new Config(); $apiKey = $config->apiKeyChatgpt; $telefono = str_replace("521", "52", $telefonoCliente); //API KEY DE CHATGPT global $conn; // Consulta SQL para obtener las conversaciones anteriores $query = "SELECT mensaje_recibido, mensaje_enviado FROM kimai2_registro WHERE telefono_wa = ? AND fecha_hora > DATE_SUB(NOW(), INTERVAL 2 HOUR) -- AND cita_creada = 0 ORDER BY fecha_hora ASC"; $stmtMensajes = $conn->prepare($query); // Vincula el parámetro "telefono" a la consulta SQL $stmtMensajes->bind_param('s', $telefono); // Ejecuta la consulta $stmtMensajes->execute(); // Obtiene el resultado $result = $stmtMensajes->get_result(); // Construye el array de mensajes anteriores $messages = array(); while ($row = $result->fetch_assoc()) { $messages[] = array("role" => "user", "content" => $row['mensaje_recibido']); $messages[] = array("role" => "assistant", "content" => $row['mensaje_enviado']); } // Agrega el mensaje del sistema y la pregunta actual del usuario array_unshift($messages, array("role" => "system", "content" => $system)); $messages[] = array("role" => "user", "content" => $pregunta); //INICIAMOS LA CONSULTA DE CURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.openai.com/v1/chat/completions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer '.$apiKey, 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array( "model" => "gpt-3.5-turbo", "messages" => $messages, "temperature" => 0 ))); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); // Obtiene información de los encabezados de la respuesta $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); curl_close($ch); // Decodificar el JSON $data = json_decode($header, true); // Obtener el valor de 'total_tokens' $totalTokens = $data['usage']['total_tokens']; curl_close($ch); $decoded_json = json_decode($response, false); //RETORNAMOS LA RESPUESTA QUE EXTRAEMOS DEL JSON $respuesta=$decoded_json->choices[0]->message->content; $respuesta = str_replace('"', '-', $respuesta); $respuesta = str_replace('\"', '-', $respuesta); $respuesta = str_replace('\\"', '-', $respuesta); $respuesta=stripcslashes($respuesta); if (strpos($respuesta, '||') !== false) { $respuestaDividida = explode('||', $respuesta); // Ahora, cada elemento del array $respuestaDividida contiene una parte de la respuesta $cliente = trim($respuestaDividida[1]); $cliente=$cliente." ".$telefono; $edad = trim($respuestaDividida[2]); $especialidad = trim($respuestaDividida[3]); $especialidad=quitar_acentos($especialidad); $listaCategoriasArray = explode(",", $listaCategorias); // quitar acentos foreach($listaCategoriasArray as $categoria){ if(strpos($especialidad, $categoria) !== false){ $especialidad = $categoria; break; } } //Si $especialidad esta vacio poner "Diagnostico" if($especialidad==""){ $especialidad="Medico general"; } $sintomas = trim($respuestaDividida[4]); require_once('crearCita.php'); $fechaCita=creaCita($cliente,$telefono, $edad, $especialidad, $sintomas); global $textoCita; //a $fechaCita darle formato dia mes año hora y minuto $fechaCita = strtotime($fechaCita); // Convierte la fecha en un timestamp de Unix setlocale(LC_TIME, 'es_ES.UTF-8'); $fechaFormateada = strftime("%A %d de %B del %Y a las %H:%M", $fechaCita); $fechaFormateada=translateDayInText($fechaFormateada); $respuesta = $textoCita." ".$fechaFormateada.", en el transcurso del dia un experto en '".$especialidad."' se pondrá en contacto con usted para confirmar la cita y darle seguimiento."; //$respuesta } //obtener la fecha y hora en string $fecha = date("Y-m-d H:i:s"); file_put_contents('responseChatgpt.txt', $fecha."-".$response."\n".$respuesta); return $respuesta; } function quitar_acentos($cadena){ $originales = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿ'; $modificadas = 'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyyby'; $cadena = utf8_decode($cadena); $cadena = strtr($cadena, utf8_decode($originales), $modificadas); return utf8_encode($cadena); } function translateDayInText($text) { // Definir la matriz de traducción $translationArray = array( "Monday" => "Lunes", "Tuesday" => "Martes", "Wednesday" => "Miércoles", "Thursday" => "Jueves", "Friday" => "Viernes", "Saturday" => "Sábado", "Sunday" => "Domingo" ); // Dividir el texto en palabras $words = explode(" ", $text); // Verificar cada palabra foreach ($words as $key => $word) { // Si la palabra es un día de la semana en inglés, traducirlo if (isset($translationArray[$word])) { $words[$key] = $translationArray[$word]; } } // Unir las palabras de nuevo en un texto $text = implode(" ", $words); return $text; }
Creamos el archivo crearCita.php
Este archivo se va encargar de crear la cita en kimai
<?php function creaCita($cliente, $telefono, $proyecto, $usuario, $descripcion) { try { global $conn; $cliente = str_replace("Cliente: ", "", $cliente); $proyecto = str_replace("Datos del auto: ", "", $proyecto); // Obtén las horas de inicio y fin del negocio $sql = "SELECT value FROM kimai2_configuration WHERE name = 'calendar.businessHours.begin'"; $result = $conn->query($sql); $row = $result->fetch_assoc(); $businessHoursBegin = new DateTime($row['value']); $sql = "SELECT value FROM kimai2_configuration WHERE name = 'calendar.businessHours.end'"; $result = $conn->query($sql); $row = $result->fetch_assoc(); $businessHoursEnd = new DateTime($row['value']); // Verifica si el cliente ya existe $sql = "SELECT id FROM `kimai2_customers` WHERE `phone` = '$telefono'"; $result = $conn->query($sql); $clienteRow = $result->fetch_assoc(); if ($clienteRow) { // Si el cliente existe, toma el ID existente $client_id = $clienteRow['id']; } else { // Si el cliente no existe, crea uno nuevo $sql = "INSERT INTO `kimai2_customers` (`name`, `phone`, `visible`, `country`, `currency`, `timezone`) VALUES ('$cliente', '$telefono', '1', 'ES', 'EUR', 'Atlantic/Canary')"; $conn->query($sql); $client_id = $conn->insert_id; // Toma el ID del cliente recién creado } // Verifica si el proyecto ya existe para este cliente $sql = "SELECT id FROM `kimai2_projects` WHERE `customer_id` = $client_id LIMIT 1"; $result = $conn->query($sql); $proyectoRow = $result->fetch_assoc(); if ($proyectoRow) { // Si el proyecto existe, toma el ID existente $project_id = $proyectoRow['id']; } else { // Si el proyecto no existe, crea uno nuevo $sql = "INSERT INTO `kimai2_projects` (`customer_id`,`name`, `visible`) VALUES ('$client_id', '$proyecto', '1')"; $conn->query($sql); $project_id = $conn->insert_id; // Toma el ID del proyecto recién creado } // Determina la cantidad de días y minutos basados en el tipo de usuario $diasAdelanto = 0; $minutos = 0; switch ($usuario) { case 'Medico general': $diasAdelanto = 1; $minutos = 60; break; case 'Cardiologia': $diasAdelanto = 1; $minutos = 60; break; case 'Pediatria': $diasAdelanto = 2; $minutos = 60; break; default: $usuario='Medico general'; $diasAdelanto = 2; $minutos = 60; } // Obtiene la fecha de hoy más el adelanto de días $fechaAdelantada = date('Y-m-d', strtotime("+$diasAdelanto days")); $start_time = $fechaAdelantada . ' ' . $businessHoursBegin->format('H:i:s'); $segundos = $minutos * 60; // Segundos de duración de la cita $end_time = date('Y-m-d H:i:s', strtotime($start_time) + $segundos); // Convertir a objetos DateTime para facilitar la comparación $startTime = new DateTime($start_time); $endTime = new DateTime($end_time); // Obtener el ID del usuario basado en el alias de usuario $sqlIdUser = "SELECT id FROM kimai2_users WHERE alias = '$usuario'"; $resultIdUser = $conn->query($sqlIdUser); $user = $resultIdUser->fetch_assoc(); $user_id = $user['id']; // Revisar cada intervalo de tiempo posible para la cita dentro del horario de trabajo while (true) { // Asegurarse que sea un día de la semana while ($startTime->format('N') >= 6) { // Saltar sábados y domingos $startTime = getNextBusinessDay($startTime); $startTime->setTime($businessHoursBegin->format('H'), $businessHoursBegin->format('i'), $businessHoursBegin->format('s')); // Configurar al inicio del horario de trabajo $endTime = clone $startTime; $endTime->modify('+' . $segundos . ' seconds'); } // Comprobar si ya existe una cita para este usuario y este intervalo de tiempo $start_time = $startTime->format('Y-m-d H:i:s'); $end_time = $endTime->format('Y-m-d H:i:s'); // Si la cita termina después del horario de trabajo, configurar al inicio del próximo día hábil $businessHoursEnd->setDate($endTime->format('Y'), $endTime->format('m'), $endTime->format('d')); if ($endTime > $businessHoursEnd) { $startTime = getNextBusinessDay($startTime); $startTime->setTime($businessHoursBegin->format('H'), $businessHoursBegin->format('i'), $businessHoursBegin->format('s')); // Configurar al inicio del horario de trabajo $endTime = clone $startTime; $endTime->modify('+' . $segundos . ' seconds'); continue; // Volver al inicio del ciclo } $sql = "SELECT * FROM `kimai2_timesheet` WHERE `user` = $user_id AND ((`start_time` >= '$start_time' AND `start_time` < '$end_time') OR (`end_time` > '$start_time' AND `end_time` <= '$end_time'))"; $result = $conn->query($sql); if ($result->num_rows > 0) { // Si ya existe una cita en este intervalo de tiempo, mover el inicio y fin de la cita al próximo intervalo posible $startTime->modify("+{$minutos} minutes"); $endTime->modify("+{$minutos} minutes"); } else { // Si no hay ninguna cita en este intervalo de tiempo, es un intervalo válido para la cita break; } } // Obtener el ID del último registro con cita_creada=1 $sql = "SELECT MAX(id) AS id FROM kimai2_registro WHERE cita_creada = 1"; $resultCita = $conn->query($sql); $rowCita= $resultCita->fetch_assoc(); $lastId = $rowCita['id']; // Si no se encontró ningún registro con cita_creada=1, seleccionar el primer registro if ($lastId === null) { $sql = "SELECT * FROM kimai2_registro ORDER BY id LIMIT 1"; } else { // Seleccionar los registros a partir del registro con ID que es uno mayor que el último ID con cita_creada=1 $sql = "SELECT * FROM kimai2_registro WHERE id > $lastId ORDER BY id"; } $resultRegistro = $conn->query($sql); $descripcion=$descripcion."\n CONVERSACION \n"; // Procesar el resultado while ($rowRegistro = $resultRegistro->fetch_assoc()) { $descripcion=$descripcion. "Mensaje Recibido: {$rowRegistro['mensaje_recibido']}\n"; $descripcion=$descripcion. "Mensaje Enviado: {$rowRegistro['mensaje_enviado']}\n"; $descripcion=$descripcion. "-------------------------\n"; } $descripcion = str_replace("'", "-", $descripcion); $sql = "INSERT INTO `kimai2_timesheet` (`user`, `activity_id`, `project_id`, `start_time`, `end_time`, `duration`, `description`, `rate`, `hourly_rate`, `timezone`, `internal_rate`, `modified_at`, `date_tz`) VALUES ('$user_id', '1', '$project_id', '$start_time', '$end_time', '$segundos', '" . $descripcion . "', '0', '0', 'Atlantic/Canary', '0', now(), now())"; $conn->query($sql); return $start_time; } catch (Exception $e) { echo 'Excepción capturada: ', $e->getMessage(), "\n"; return null; } } function getNextBusinessDay($date) { do { $date->modify('+1 day'); } while ($date->format('N') >= 6); // Saltar sábados y domingos return $date; }
Listo hemos terminado, por ultimo te voy a dejar todos los códigos
index.php
<?php /* * VERIFICACION DEL WEBHOOK */ //TOQUEN QUE QUERRAMOS PONER $token = 'HolaNovato'; //RETO QUE RECIBIREMOS DE FACEBOOK $palabraReto = $_GET['hub_challenge']; //TOQUEN DE VERIFICACION QUE RECIBIREMOS DE FACEBOOK $tokenVerificacion = $_GET['hub_verify_token']; //SI EL TOKEN QUE GENERAMOS ES EL MISMO QUE NOS ENVIA FACEBOOK RETORNAMOS EL RETO PARA VALIDAR QUE SOMOS NOSOTROS if ($token === $tokenVerificacion) { echo $palabraReto; exit; } $textoCita="He creado su cita para el dia"; /* * RECEPCION DE MENSAJES */ //LEEMOS LOS DATOS ENVIADOS POR WHATSAPP $respuesta = file_get_contents("php://input"); //CONVERTIMOS EL JSON EN ARRAY DE PHP $respuesta = json_decode($respuesta, true); //EXTRAEMOS EL MENSAJE DEL ARRAY $mensaje = $respuesta['entry'][0]['changes'][0]['value']['messages'][0]['text']['body']; //EXTRAEMOS EL TELEFONO DEL ARRAY $telefonoCliente = $respuesta['entry'][0]['changes'][0]['value']['messages'][0]['from']; //EXTRAEMOS EL ID DE WHATSAPP DEL ARRAY $id = $respuesta['entry'][0]['changes'][0]['value']['messages'][0]['id']; //EXTRAEMOS EL TIEMPO DE WHATSAPP DEL ARRAY $timestamp = $respuesta['entry'][0]['changes'][0]['value']['messages'][0]['timestamp']; //SI HAY UN MENSAJE if ($mensaje != null) { $pregunta = $mensaje; require_once("conexion.php"); // Prepara la consulta SQL $queryCompania = 'SELECT company, address, contact FROM kimai2_invoice_templates WHERE id = ?'; $stmtCompania = $conn->prepare($queryCompania); // Vincula el parámetro "id" a la consulta SQL $id_inv = 1; $stmtCompania->bind_param('i', $id_inv); // Ejecuta la consulta $stmtCompania->execute(); // Obtiene el resultado $resultCompania = $stmtCompania->get_result(); // Obtiene los datos del registro $rowCompania = $resultCompania->fetch_assoc(); // Prepara la consulta SQL // Crea la consulta SQL $queryCategoria = "SELECT alias FROM kimai2_users WHERE enabled = 1 AND id != 1"; // Prepara la consulta $stmtCategoria = $conn->prepare($queryCategoria); // Ejecuta la consulta $stmtCategoria->execute(); // Obtiene el resultado $resultCategoria = $stmtCategoria->get_result(); // Crea un array para guardar los alias $listaCategorias = ""; // Recorre los resultados y añade cada alias al array while ($row = $resultCategoria->fetch_assoc()) { $listaCategorias = $listaCategorias.$row['alias'].","; } $listaCategorias = rtrim($listaCategorias, ","); $system="Hola, soy un asesor de Información de la clínica ".$rowCompania['company']. ", no proporciono citas pero genero reportes para que posteriormente un experto agende una cita, no doy recomendaciones para curar, recomiendo solo mi clínica y pido un dato a la vez.". " Mis tareas incluyen recibir información, escribir reportes y no hay necesidad de que el paciente lo sepa. Aquí está el procedimiento que sigo: ". "1. Primero, pido el nombre. ". "2. Luego, pido la edad. ". "3. Después, pregunto qué sintomas tiene y en base a las respuestas defino en cuál de estas especialidades cae: ". $listaCategorias.".". "Si no cae en ninguna especialidad colocarlo en Medico general.". "Una vez recolectada la información sobre los síntomas, no programo ninguna cita. En cambio, escribo un reporte con este formato: ". "||paciente||etapa_edad||especialidad||sintomas|| (reemplaza el contenido entre || por la información recolectada).". //"||cliente||datos_auto||categoria_falla||descripcion_falla|| (reemplaza el contenido entre || por la información recolectada).". "Es vital que siempre que detecte sintomas, escriba el reporte en el formato mencionado para que un experto agende una cita. ". "Si el paciente me pide una cita le debo escribir el reporte con el formato mencionado. ". "Importante: No invento ni añado ninguna información que no se me haya proporcionado. Todo mi trabajo se basa en los datos que recibo."; "Solo si me lo piden doy informacion de la empresa, Nombre de la empresa: " . $rowCompania['company'] . ", Ubicación: " . $rowCompania['address'] . " " . $rowCompania['contact'] . ". "; require_once "chatgpt.php"; $respuesta = preguntaChatgpt($system, $pregunta, $telefonoCliente,$listaCategorias); //ESCRIBIMOS LA RESPUESTA file_put_contents("respuesta.txt", $respuesta); require_once "whatsapp.php"; //ENVIAMOS LA RESPUESTA VIA WHATSAPP enviar($mensaje, $respuesta, $id, $timestamp, $telefonoCliente); }
conexion.php
<?php require __DIR__ . '/../../vendor/autoload.php'; $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../..'); $dotenv->load(); $databaseUrl = $_ENV['DATABASE_URL']; $components = parse_url($databaseUrl); $dbName = ltrim($components['path'], '/'); // Crear la conexión $conn = new mysqli($components['host'], $components['user'], $components['pass'], $dbName); if ($conn->connect_error) { die("Error de conexión: " . $conn->connect_error); }
config.php
<?php class Config { public $apiKeyChatgpt; public $tokenWa; public $telefonoIDWa; public $remoto; public function __construct() { $remoto=true; if ($remoto) { // Coloca estos valores si remoto es true $this->apiKeyChatgpt = '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'; $this->tokenWa = 'EAANIQCCaMQIBAM1cIHPp640kaa6fbVXNxyoiGEAQYubPdlI0PsmfTEiZBWZCENX1ZAyGDIC3IHiu3meSGiDU6tTg2Kyam7yjOPVXZAyYpNkrB4byIldswdGmwv1nAZABD3MaO2a5nD4TPe3Dhgbbpd9AGpzylWyrhp0ZBqKFNIvkcwKh8t87QlzZC34fsmpNlni1Eu9slLLRwZDZD'; $this->telefonoIDWa = '116907067953774'; } else { // Coloca estos valores si remoto es false $this->apiKeyChatgpt = 'otro_valor'; $this->tokenWa = 'otro_valor'; $this->telefonoIDWa = 'otro_valor'; } } }
chatgpt.php
<?php function preguntaChatgpt($system, $pregunta, $telefonoCliente,$listaCategorias){ require "config.php"; $config = new Config(); $apiKey = $config->apiKeyChatgpt; $telefono = str_replace("521", "52", $telefonoCliente); //API KEY DE CHATGPT global $conn; // Consulta SQL para obtener las conversaciones anteriores $query = "SELECT mensaje_recibido, mensaje_enviado FROM kimai2_registro WHERE telefono_wa = ? AND fecha_hora > DATE_SUB(NOW(), INTERVAL 2 HOUR) -- AND cita_creada = 0 ORDER BY fecha_hora ASC"; $stmtMensajes = $conn->prepare($query); // Vincula el parámetro "telefono" a la consulta SQL $stmtMensajes->bind_param('s', $telefono); // Ejecuta la consulta $stmtMensajes->execute(); // Obtiene el resultado $result = $stmtMensajes->get_result(); // Construye el array de mensajes anteriores $messages = array(); while ($row = $result->fetch_assoc()) { $messages[] = array("role" => "user", "content" => $row['mensaje_recibido']); $messages[] = array("role" => "assistant", "content" => $row['mensaje_enviado']); } // Agrega el mensaje del sistema y la pregunta actual del usuario array_unshift($messages, array("role" => "system", "content" => $system)); $messages[] = array("role" => "user", "content" => $pregunta); //INICIAMOS LA CONSULTA DE CURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.openai.com/v1/chat/completions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer '.$apiKey, 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array( "model" => "gpt-3.5-turbo", "messages" => $messages, "temperature" => 0 ))); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); // Obtiene información de los encabezados de la respuesta $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); curl_close($ch); // Decodificar el JSON $data = json_decode($header, true); // Obtener el valor de 'total_tokens' $totalTokens = $data['usage']['total_tokens']; curl_close($ch); $decoded_json = json_decode($response, false); //RETORNAMOS LA RESPUESTA QUE EXTRAEMOS DEL JSON $respuesta=$decoded_json->choices[0]->message->content; $respuesta = str_replace('"', '-', $respuesta); $respuesta = str_replace('\"', '-', $respuesta); $respuesta = str_replace('\\"', '-', $respuesta); $respuesta=stripcslashes($respuesta); if (strpos($respuesta, '||') !== false) { $respuestaDividida = explode('||', $respuesta); // Ahora, cada elemento del array $respuestaDividida contiene una parte de la respuesta $cliente = trim($respuestaDividida[1]); $cliente=$cliente." ".$telefono; $edad = trim($respuestaDividida[2]); $especialidad = trim($respuestaDividida[3]); $especialidad=quitar_acentos($especialidad); $listaCategoriasArray = explode(",", $listaCategorias); // quitar acentos foreach($listaCategoriasArray as $categoria){ if(strpos($especialidad, $categoria) !== false){ $especialidad = $categoria; break; } } //Si $especialidad esta vacio poner "Diagnostico" if($especialidad==""){ $especialidad="Medico general"; } $sintomas = trim($respuestaDividida[4]); require_once('crearCita.php'); $fechaCita=creaCita($cliente,$telefono, $edad, $especialidad, $sintomas); global $textoCita; //a $fechaCita darle formato dia mes año hora y minuto $fechaCita = strtotime($fechaCita); // Convierte la fecha en un timestamp de Unix setlocale(LC_TIME, 'es_ES.UTF-8'); $fechaFormateada = strftime("%A %d de %B del %Y a las %H:%M", $fechaCita); $fechaFormateada=translateDayInText($fechaFormateada); $respuesta = $textoCita." ".$fechaFormateada.", en el transcurso del dia un experto en '".$especialidad."' se pondrá en contacto con usted para confirmar la cita y darle seguimiento."; //$respuesta } //obtener la fecha y hora en string $fecha = date("Y-m-d H:i:s"); file_put_contents('responseChatgpt.txt', $fecha."-".$response."\n".$respuesta); return $respuesta; } function quitar_acentos($cadena){ $originales = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿ'; $modificadas = 'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyyby'; $cadena = utf8_decode($cadena); $cadena = strtr($cadena, utf8_decode($originales), $modificadas); return utf8_encode($cadena); } function translateDayInText($text) { // Definir la matriz de traducción $translationArray = array( "Monday" => "Lunes", "Tuesday" => "Martes", "Wednesday" => "Miércoles", "Thursday" => "Jueves", "Friday" => "Viernes", "Saturday" => "Sábado", "Sunday" => "Domingo" ); // Dividir el texto en palabras $words = explode(" ", $text); // Verificar cada palabra foreach ($words as $key => $word) { // Si la palabra es un día de la semana en inglés, traducirlo if (isset($translationArray[$word])) { $words[$key] = $translationArray[$word]; } } // Unir las palabras de nuevo en un texto $text = implode(" ", $words); return $text; }
whatsapp.php
<?php //enviar.php /* * RECIBIMOS LA RESPUESTA */ function enviar($recibido, $enviadoWa, $idWA, $timestamp, $telefonoCliente) { global $conn; global $textoCita; //CONSULTAMOS TODOS LOS REGISTROS CON EL ID DEL MANSAJE $sqlCantidad = "SELECT count(id) AS cantidad FROM kimai2_registro WHERE id_wa='" . $idWA . "';"; $resultCantidad = $conn->query($sqlCantidad); //OBTENEMOS LA CANTIDAD DE MENSAJES ENCONTRADOS (SI ES 0 LO REGISTRAMOS SI NO NO) $cantidad = 0; //SI LA CONSULTA ARROJA RESULTADOS if ($resultCantidad) { //OBTENEMOS EL PRIMER REGISTRO $rowCantidad = $resultCantidad->fetch_row(); //OBTENEMOS LA CANTIDAD DE REGISTROS $cantidad = $rowCantidad[0]; } //SI LA CANTIDAD DE REGISTROS ES 0 ENVIAMOS EL MENSAJE DE LO CONTRARIO NO LO ENVIAMOS PORQUE YA SE ENVIO if ($cantidad == 0) { $enviado = str_replace("\n", "", $enviadoWa); //TOKEN QUE NOS DA FACEBOOK $config = new Config(); $token = $config->tokenWa; //NUESTRO TELEFONO $telefono = str_replace("521", "52", $telefonoCliente); //IDENTIFICADOR DE NÚMERO DE TELÉFONO $telefonoID = $config->telefonoIDWa; //URL A DONDE SE MANDARA EL MENSAJE $url = 'https://graph.facebook.com/v16.0/' . $telefonoID . '/messages'; //CONFIGURACION DEL MENSAJE $mensaje = '' . '{' . '"messaging_product": "whatsapp", ' . '"recipient_type": "individual",' . '"to": "' . $telefono . '", ' . '"type": "text", ' . '"text": ' . '{' . ' "body":"' . $enviado . '",' . ' "preview_url": true, ' . '} ' . '}'; //DECLARAMOS LAS CABECERAS $header = array("Authorization: Bearer " . $token, "Content-Type: application/json",); //INICIAMOS EL CURL $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POSTFIELDS, $mensaje); curl_setopt($curl, CURLOPT_HTTPHEADER, $header); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //OBTENEMOS LA RESPUESTA DEL ENVIO DE INFORMACION $responseString = curl_exec($curl); $response = json_decode($responseString, true); $fecha = date("Y-m-d H:i:s"); file_put_contents('responseWhatsapp.txt', $fecha . "-" . $responseString); //OBTENEMOS EL CODIGO DE LA RESPUESTA $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); //CERRAMOS EL CURL curl_close($curl); global $textoCita; if (strpos($enviado, $textoCita) !== false) { $cita_creada = "1"; } else { $cita_creada = "0"; } try { //Cambiar el texto de la cita para que chatgpt no detecte que fue una cita $enviado = str_replace($textoCita, "Un asesor se pondrá en contacto con usted el día", $enviado); //INSERTAMOS LOS REGISTROS DEL ENVIO DEL WHATSAPP $sql = "INSERT INTO kimai2_registro (mensaje_recibido, mensaje_enviado, id_wa, timestamp_wa, telefono_wa,cita_creada) VALUES (?, ?, ?, ?, ?, ?)"; $stmt = $conn->prepare($sql); $stmt->bind_param('ssssss', $recibido, $enviado, $idWA, $timestamp, $telefono, $cita_creada); $result = $stmt->execute(); if ($result) { echo "El registro se insertó correctamente"; } else { $error = $stmt->error; echo "Error al insertar el registro: " . $error; } } catch (Exception $e) { $error = $e->getMessage(); echo "Error al insertar el registro: " . $error; } } }
crearCita.php
<?php function creaCita($cliente, $telefono, $proyecto, $usuario, $descripcion) { try { global $conn; $cliente = str_replace("Cliente: ", "", $cliente); $proyecto = str_replace("Datos del auto: ", "", $proyecto); // Obtén las horas de inicio y fin del negocio $sql = "SELECT value FROM kimai2_configuration WHERE name = 'calendar.businessHours.begin'"; $result = $conn->query($sql); $row = $result->fetch_assoc(); $businessHoursBegin = new DateTime($row['value']); $sql = "SELECT value FROM kimai2_configuration WHERE name = 'calendar.businessHours.end'"; $result = $conn->query($sql); $row = $result->fetch_assoc(); $businessHoursEnd = new DateTime($row['value']); // Verifica si el cliente ya existe $sql = "SELECT id FROM `kimai2_customers` WHERE `phone` = '$telefono'"; $result = $conn->query($sql); $clienteRow = $result->fetch_assoc(); if ($clienteRow) { // Si el cliente existe, toma el ID existente $client_id = $clienteRow['id']; } else { // Si el cliente no existe, crea uno nuevo $sql = "INSERT INTO `kimai2_customers` (`name`, `phone`, `visible`, `country`, `currency`, `timezone`) VALUES ('$cliente', '$telefono', '1', 'ES', 'EUR', 'Atlantic/Canary')"; $conn->query($sql); $client_id = $conn->insert_id; // Toma el ID del cliente recién creado } // Verifica si el proyecto ya existe para este cliente $sql = "SELECT id FROM `kimai2_projects` WHERE `customer_id` = $client_id LIMIT 1"; $result = $conn->query($sql); $proyectoRow = $result->fetch_assoc(); if ($proyectoRow) { // Si el proyecto existe, toma el ID existente $project_id = $proyectoRow['id']; } else { // Si el proyecto no existe, crea uno nuevo $sql = "INSERT INTO `kimai2_projects` (`customer_id`,`name`, `visible`) VALUES ('$client_id', '$proyecto', '1')"; $conn->query($sql); $project_id = $conn->insert_id; // Toma el ID del proyecto recién creado } // Determina la cantidad de días y minutos basados en el tipo de usuario $diasAdelanto = 0; $minutos = 0; switch ($usuario) { case 'Medico general': $diasAdelanto = 1; $minutos = 60; break; case 'Cardiologia': $diasAdelanto = 1; $minutos = 60; break; case 'Pediatria': $diasAdelanto = 2; $minutos = 60; break; default: $usuario='Medico general'; $diasAdelanto = 2; $minutos = 60; } // Obtiene la fecha de hoy más el adelanto de días $fechaAdelantada = date('Y-m-d', strtotime("+$diasAdelanto days")); $start_time = $fechaAdelantada . ' ' . $businessHoursBegin->format('H:i:s'); $segundos = $minutos * 60; // Segundos de duración de la cita $end_time = date('Y-m-d H:i:s', strtotime($start_time) + $segundos); // Convertir a objetos DateTime para facilitar la comparación $startTime = new DateTime($start_time); $endTime = new DateTime($end_time); // Obtener el ID del usuario basado en el alias de usuario $sqlIdUser = "SELECT id FROM kimai2_users WHERE alias = '$usuario'"; $resultIdUser = $conn->query($sqlIdUser); $user = $resultIdUser->fetch_assoc(); $user_id = $user['id']; // Revisar cada intervalo de tiempo posible para la cita dentro del horario de trabajo while (true) { // Asegurarse que sea un día de la semana while ($startTime->format('N') >= 6) { // Saltar sábados y domingos $startTime = getNextBusinessDay($startTime); $startTime->setTime($businessHoursBegin->format('H'), $businessHoursBegin->format('i'), $businessHoursBegin->format('s')); // Configurar al inicio del horario de trabajo $endTime = clone $startTime; $endTime->modify('+' . $segundos . ' seconds'); } // Comprobar si ya existe una cita para este usuario y este intervalo de tiempo $start_time = $startTime->format('Y-m-d H:i:s'); $end_time = $endTime->format('Y-m-d H:i:s'); // Si la cita termina después del horario de trabajo, configurar al inicio del próximo día hábil $businessHoursEnd->setDate($endTime->format('Y'), $endTime->format('m'), $endTime->format('d')); if ($endTime > $businessHoursEnd) { $startTime = getNextBusinessDay($startTime); $startTime->setTime($businessHoursBegin->format('H'), $businessHoursBegin->format('i'), $businessHoursBegin->format('s')); // Configurar al inicio del horario de trabajo $endTime = clone $startTime; $endTime->modify('+' . $segundos . ' seconds'); continue; // Volver al inicio del ciclo } $sql = "SELECT * FROM `kimai2_timesheet` WHERE `user` = $user_id AND ((`start_time` >= '$start_time' AND `start_time` < '$end_time') OR (`end_time` > '$start_time' AND `end_time` <= '$end_time'))"; $result = $conn->query($sql); if ($result->num_rows > 0) { // Si ya existe una cita en este intervalo de tiempo, mover el inicio y fin de la cita al próximo intervalo posible $startTime->modify("+{$minutos} minutes"); $endTime->modify("+{$minutos} minutes"); } else { // Si no hay ninguna cita en este intervalo de tiempo, es un intervalo válido para la cita break; } } // Obtener el ID del último registro con cita_creada=1 $sql = "SELECT MAX(id) AS id FROM kimai2_registro WHERE cita_creada = 1"; $resultCita = $conn->query($sql); $rowCita= $resultCita->fetch_assoc(); $lastId = $rowCita['id']; // Si no se encontró ningún registro con cita_creada=1, seleccionar el primer registro if ($lastId === null) { $sql = "SELECT * FROM kimai2_registro ORDER BY id LIMIT 1"; } else { // Seleccionar los registros a partir del registro con ID que es uno mayor que el último ID con cita_creada=1 $sql = "SELECT * FROM kimai2_registro WHERE id > $lastId ORDER BY id"; } $resultRegistro = $conn->query($sql); $descripcion=$descripcion."\n CONVERSACION \n"; // Procesar el resultado while ($rowRegistro = $resultRegistro->fetch_assoc()) { $descripcion=$descripcion. "Mensaje Recibido: {$rowRegistro['mensaje_recibido']}\n"; $descripcion=$descripcion. "Mensaje Enviado: {$rowRegistro['mensaje_enviado']}\n"; $descripcion=$descripcion. "-------------------------\n"; } $descripcion = str_replace("'", "-", $descripcion); $sql = "INSERT INTO `kimai2_timesheet` (`user`, `activity_id`, `project_id`, `start_time`, `end_time`, `duration`, `description`, `rate`, `hourly_rate`, `timezone`, `internal_rate`, `modified_at`, `date_tz`) VALUES ('$user_id', '1', '$project_id', '$start_time', '$end_time', '$segundos', '" . $descripcion . "', '0', '0', 'Atlantic/Canary', '0', now(), now())"; $conn->query($sql); return $start_time; } catch (Exception $e) { echo 'Excepción capturada: ', $e->getMessage(), "\n"; return null; } } function getNextBusinessDay($date) { do { $date->modify('+1 day'); } while ($date->format('N') >= 6); // Saltar sábados y domingos return $date; }
Ave que vuela, a la cazuela.