// ================================================================== // JS.JS - VERSIÓN FINAL CON GESTIÓN DE CONTEXTO (BOT vs AGENTE) // ================================================================== try { var context = atob(JSON.parse(atob(WolkvoxChat)).context); } catch (error) { // console.error(error.message); } var base_url = base_url; var url = avatar; var avatarBot = avatarBot; var transparent = transparent; var agente_asignado = ""; var agente = ""; var session = idsessionNew; var session2 = idsessionNew; var audio = new Audio('https://storage.googleapis.com/wvx-digital-widget-staticresources-pdn/sounds/new.wav'); var recibir = ''; var lastRoutingReentrySkill = ''; var write = ''; var from_chat = ''; var idmsg = 0; var title = document.title; var newTitle = ''; var count = 0; var INDEX = 0; var heightFrameWidget = ""; var widthFrameWidget = ""; var chatCircleImgWidthSend = chatCircleImgWidthSend; var chatCircleImgHeightSend = chatCircleImgHeightSend; var heightFrameChat = ""; var widthFrameChat = ""; var type_call = ""; // Flag global para saber si hay una llamada activa (audio/video). // Se actualiza desde js.js (inicio) y clicktocall.js (estados SIP). window.wvx_call_active = window.wvx_call_active || false; var chat_id = 0; var atstrt = 0; var startedChat = 0; var height_mobile = height_mobile; function wvxCanAutoRedirectSurvey(linkEl) { try { if (!linkEl || linkEl.tagName.toLowerCase() !== 'a') return false; // Bloquear auto-redirect durante llamada activa (defensa en profundidad) if (window.wvx_call_active || type_call) return false; // Si el backend envía la URL sin "campana", no abrir pestaña automáticamente if (linkEl.href) { var u = new URL(linkEl.href, window.location.href); if (u.searchParams.has('campana') && !u.searchParams.get('campana')) return false; } return true; } catch (e) { // Si no se puede parsear la URL, no auto-redirigir return false; } } var pollerInterval = null; // Para controlar el timer del polling var lastDisplayedMessageId = 0; // Para saber cuál fue el último mensaje mostrado // SISTEMA DE POLLING HÍBRIDO INTELIGENTE var pollerConfig = { frequencies: { ACTIVE: 3000, // 3 segundos - cuando hay actividad o mensajes pendientes SEMI_ACTIVE: 15000, // 15 segundos - actividad moderada INACTIVE: 60000, // 60 segundos - poca actividad STOPPED: null // Detenido completamente }, currentFrequency: 3000, lastActivity: Date.now(), maxInactiveTime: 23 * 60 * 60 * 1000, // 23 horas hasPendingMessages: false }; var iti; var init_routing = 0; // FUNCIÓN PARA DETERMINAR LA FRECUENCIA DE POLLING INTELIGENTE function determinePollingFrequency() { var now = Date.now(); var timeSinceActivity = now - pollerConfig.lastActivity; // Si hay mensajes pendientes, mantener frecuencia activa if (pollerConfig.hasPendingMessages) { return pollerConfig.frequencies.ACTIVE; } // Si ha pasado demasiado tiempo, detener if (timeSinceActivity > pollerConfig.maxInactiveTime) { return pollerConfig.frequencies.STOPPED; } // Frecuencias adaptativas basadas en tiempo de inactividad if (timeSinceActivity < 2 * 60 * 1000) { // Menos de 2 minutos return pollerConfig.frequencies.ACTIVE; } else if (timeSinceActivity < 5 * 60 * 1000) { // Menos de 5 minutos return pollerConfig.frequencies.SEMI_ACTIVE; } else { return pollerConfig.frequencies.INACTIVE; } } // FUNCIÓN PARA ACTUALIZAR LA ACTIVIDAD DEL USUARIO function updateUserActivity() { pollerConfig.lastActivity = Date.now(); } // FUNCIÓN PRINCIPAL DE POLLING INTELIGENTE function startIntelligentPoller(sessionId, operationId) { // Detener cualquier poller existente if (pollerInterval) { clearInterval(pollerInterval); pollerInterval = null; } // Actualizar actividad updateUserActivity(); // Función recursiva para polling adaptativo function scheduleNextPoll() { var frequency = determinePollingFrequency(); if (frequency === pollerConfig.frequencies.STOPPED) { pollerInterval = null; return; } // Si la frecuencia cambió, actualizarla if (frequency !== pollerConfig.currentFrequency) { pollerConfig.currentFrequency = frequency; } // Programar siguiente ejecución pollerInterval = setTimeout(function () { pollForNewMessages(sessionId, operationId); scheduleNextPoll(); // Programar la siguiente iteración }, frequency); } // Ejecutar primera consulta inmediatamente pollForNewMessages(sessionId, operationId); // Programar siguientes consultas scheduleNextPoll(); } switch (widget) { case "l": heightFrameWidget = '71'; widthFrameWidget = '75'; break; case "m": heightFrameWidget = '55'; widthFrameWidget = '60'; break; case "s": heightFrameWidget = '45'; widthFrameWidget = '50'; break; } switch (chat) { case "l": heightFrameChat = '543'; widthFrameChat = '288'; break; case "m": heightFrameChat = '455'; widthFrameChat = '288'; break; case "s": heightFrameChat = '400'; widthFrameChat = '288'; break; case "full": heightFrameChat = '100%'; widthFrameChat = '100%'; document.getElementById("chat-logs").style.height == "78% !importan" break; } var device = getDeviceType(); if (device == "mobile") { widthFrameChat = '100%'; heightFrameChat = height_mobile ? height_mobile + '%' : '100%'; } $id_config = id_config; $skill_bd = skill; $skill = $("#skill").val(); $text4 = ''; $prefA = ''; $routing = ''; if (transparent == 1) { heightFrameWidget = (parseInt(chatCircleImgHeightSend) + parseInt(20)); widthFrameWidget = (parseInt(chatCircleImgWidthSend) + parseInt(20)); } function popUpAlert(text) { document.getElementById("popUpChat_chat_micro_alert_txt").innerHTML = "" + text + ""; document.getElementById("popUpChat_chat_micro_alert").classList.toggle("show"); } // Nueva función para mostrar notificaciones no bloqueantes tipo toast function showToastNotification(message, type = 'error', duration = 4000) { // Crear el elemento toast si no existe var toastId = 'chat-toast-notification'; var toast = document.getElementById(toastId); if (!toast) { toast = document.createElement('div'); toast.id = toastId; toast.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background-color: ${type === 'error' ? '#f44336' : '#4CAF50'}; color: white; padding: 16px 24px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 10000; font-family: Custom-Condensed, Arial, sans-serif; font-size: 14px; max-width: 90%; text-align: center; opacity: 0; transition: opacity 0.3s ease-in-out; pointer-events: none; `; document.body.appendChild(toast); } // Actualizar mensaje y estilo según el tipo toast.innerHTML = '' + message + ''; toast.style.backgroundColor = type === 'error' ? '#f44336' : '#4CAF50'; // Mostrar toast con animación setTimeout(() => { toast.style.opacity = '1'; }, 10); // Ocultar automáticamente después de la duración especificada setTimeout(() => { toast.style.opacity = '0'; }, duration); } function cargarDatosLocalStorage(event) { var s_value; try { if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text1'))) { $("#text1").val(s_value); $("#textNombre").addClass("mdc-floating-label--float-above"); // $("#textNombre").css('background-color', '#fff'); // $("#textNombre").css('padding','0 4px;'); } if (event == "closed") { if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text2'))) { iti.setNumber(s_value); } } else { if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text2'))) { $("#textPhone").addClass("mdc-floating-label--float-above"); // $("#textPhone").css('background-color', '#fff'); // $("#textPhone").css('padding','0 4px;'); } } if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text3'))) { $("#text3").val(s_value); $("#textEmail").addClass("mdc-floating-label--float-above"); // $("#textEmail").css('background-color', '#fff'); // $("#textEmail").css('padding','0 4px;'); } if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text4'))) { $("#text4").val(s_value); $("#textIdentification").addClass("mdc-floating-label--float-above"); // $("#textIdentification").css('background-color', '#fff'); // $("#textIdentification").css('padding','0 4px;'); } if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text5'))) { $("#text5").val(s_value); // $("#text5").addClass("mdc-floating-label--float-above"); // $("#text5").css('background-color', '#fff'); // $("#text5").css('padding','0 4px;'); } if ((s_value = window.localStorage.getItem('chat.wolkvox.form.text6'))) { $("#text6").val(s_value); // $("#text6").addClass("mdc-floating-label--float-above"); // $("#text6").css('background-color', '#fff'); // $("#text6").css('padding','0 4px;'); } } catch (error) { } } function sleep(milliseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) { break; } } } var count = 3; do { if (typeof mdc !== "undefined") { mdc.autoInit(); count = 0; } else { sleep(2000); count--; } } while (count != 0); $(document).ready(function () { cargarDatosLocalStorage("init"); // enforceFormat y formatToPhone removidos: conflictaban con el filtro de dígitos // delegado en document y formateaban con estilo US incompatible con teléfonos locales. // document.addEventListener('input', function (event) { // Verificar si el elemento que disparó el evento es un input if (event.target.tagName.toLowerCase() === 'input') { // Verificar si el input es de tipo "file" if (event.target.type.toLowerCase() === 'file') { // No hagas nada con inputs de tipo "file" return; } // Obtener el valor actual del input var valorInput = event.target.value; // Sanitizar el valor del input var valorSanitizado = DOMPurify.sanitize(valorInput); // Asignar el valor sanitizado de vuelta al input event.target.value = valorSanitizado; } }); // }); $('input[type="file"]').change( function (e) { var file = e.target.files; var completeSize = 0; for (var i = 0; i < file.length; i++) { completeSize = completeSize + file[i].size; } if (completeSize > 5242880) { showToastNotification("El archivo es demasiado grande. Tamaño máximo: 5MB", 'error', 4000); document.getElementById("chat_input_attach").value = ""; return; } sendFileAttached(file); } ); function sendFileAttached(file) { var formData = new FormData(); for (var i = 0; i < file.length; i++) { formData.append(i, file[i]); } formData.append('ses', session); formData.append('operation', $("#idcliente").val()); formData.append('skill', $("#skill").val()); var uriAttach = ""; $.ajax({ url: "querys/sendAttach.php", data: formData, type: "POST", dataType: "json", processData: false, contentType: false, beforeSend: function () { document.getElementById("alerta").innerHTML = "Uploading..."; document.getElementById("alerta").style.display = ""; }, success: (data) => { sendMsgType = data.sendMsgType; if (typeof data.error != "undefined") { showToastNotification(data.error, 'error', 4000); document.getElementById("alerta").innerHTML = "" + data.error + ""; } else { for (var i = 0; i <= data.name.length - 1; i++) { generate_message(data.name[i], 'self', " ", " ", data.data[i]); } document.getElementById("alerta").innerHTML = "Uploaded"; } setTimeout(function () { document.getElementById("alerta").style.display = "none"; document.getElementById("alerta").innerHTML = ""; }, 1200); document.getElementById("chat_input_attach").value = ""; uriAttach = data.ext + "|" + data.data; if (sendMsgType == 1) {//revisar $msg = uriAttach; $idcliente = $idcliente.replace(" ", "");//revisar $skill = $skill.replace(" ", "");//revisar chatbotmsg($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $msg, ""); } }, error: function (xhr, status, error) { console.error("AJAX Error Details:", status, error, xhr.responseText); document.getElementById("alerta").innerHTML = "loading..."; setTimeout(function () { document.getElementById("alerta").style.display = "none"; document.getElementById("alerta").innerHTML = ""; }, 1200); document.getElementById("chat_input_attach").value = ""; } }); } function iframeResize(height, width, bottom) { parent.postMessage("resize::" + height + "||" + width + "||1%||" + bottom, "*"); } function setInputFilter(textbox, inputFilter) { ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function (event) { textbox.addEventListener(event, function () { if (inputFilter(this.value)) { this.oldValue = this.value; this.oldSelectionStart = this.selectionStart; this.oldSelectionEnd = this.selectionEnd; } else if (this.hasOwnProperty("oldValue")) { this.value = this.oldValue; this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd); } }); }); } // Filtro de solo dígitos para el campo de teléfono - usa event delegation para evitar // condiciones de carrera donde text2 aún no existe cuando js.js se ejecuta. (function () { var phoneDigitFilter = /^\d*$/; ["keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function (eventName) { document.addEventListener(eventName, function (e) { var el = e.target; if (!el || el.id !== "text2") return; if (phoneDigitFilter.test(el.value)) { el._oldPhoneValue = el.value; el._oldPhoneSelStart = el.selectionStart; el._oldPhoneSelEnd = el.selectionEnd; } else if (el.hasOwnProperty("_oldPhoneValue")) { el.value = el._oldPhoneValue; el.setSelectionRange(el._oldPhoneSelStart, el._oldPhoneSelEnd); } }); }); document.addEventListener("input", function (e) { var el = e.target; if (!el || el.id !== "text2") return; if (phoneDigitFilter.test(el.value)) { el._oldPhoneValue = el.value; el._oldPhoneSelStart = el.selectionStart; el._oldPhoneSelEnd = el.selectionEnd; } else if (el.hasOwnProperty("_oldPhoneValue")) { el.value = el._oldPhoneValue; el.setSelectionRange(el._oldPhoneSelStart, el._oldPhoneSelEnd); } var maxLen = (typeof window.phoneMaxLength !== 'undefined' && window.phoneMaxLength > 0) ? window.phoneMaxLength : 0; var minLen = (typeof window.phoneMinLength !== 'undefined' && window.phoneMinLength > 0) ? window.phoneMinLength : maxLen; // si solo se configura el máximo, se usa como umbral mínimo para el aviso var hintEl = document.getElementById("phone-hint"); if (maxLen > 0 && el.value.length > maxLen) { el.value = el.value.slice(0, maxLen); } if (!hintEl) { var phoneLabel = document.getElementById("test") || el.closest("label"); if (phoneLabel && phoneLabel.parentNode) { hintEl = document.createElement("div"); hintEl.setAttribute("id", "phone-hint"); phoneLabel.parentNode.insertBefore(hintEl, phoneLabel.nextSibling); } } if (!hintEl) return; if (minLen > 0 && el.value.length > 0 && el.value.length < minLen) { hintEl.textContent = "Mínimo " + minLen + " dígitos requeridos"; hintEl.style.display = "block"; } else { hintEl.style.display = "none"; hintEl.textContent = ""; } }); })(); $("#enviar_form_chat").on("click", validate); $(document).on("click", "#start_chat_options", function () { wvxUserChoseChat = true; submit_form(); }); $("#btn-audio").on("click", clicktocall_audio); $("#btn-video").on("click", clicktocall_video); $("#btn-callback").on("click", callback); $('#record-submit').click(startRecording); $('#stop-record-submit').click(stopRecording); $('#cancel-record-submit').click(cancelRecording); function validateEmail(email) { var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); } function clicktocall_audio() { $operation = $("#idcliente").val(); $pais_selected = document.getElementsByClassName("iti__selected-flag")[0].title.replace(/[^0-9]+/g, ""); $text2 = $pais_selected; $text2 += $("#text2").val(); document.getElementById("divLoad").style.display = ""; type_call = "call-audio"; window.wvx_call_active = true; $text1 = $("#text1").val(); $text3 = $("#text3").val(); $text4 = $text4 == '' && typeof $text4 !== 'undefined' ? $("#text4").val() : $text4; $text5 = $("#text5").val(); $text6 = $("#text6").val(); $text1 = typeof $text1 !== 'undefined' ? $text1.replace(/(<([^>]+)>)/ig, "") : ''; $text2 = typeof $text2 !== 'undefined' ? $text2.replace(/(<([^>]+)>)/ig, "") : ''; $text3 = typeof $text3 !== 'undefined' ? $text3.replace(/(<([^>]+)>)/ig, "") : ''; $text4 = typeof $text4 !== 'undefined' ? $text4.replace(/(<([^>]+)>)/ig, "") : ''; $text5 = typeof $text5 !== 'undefined' ? $text5.replace(/(<([^>]+)>)/ig, "") : ''; $text6 = typeof $text6 !== 'undefined' ? $text6.replace(/(<([^>]+)>)/ig, "") : ''; conexion_sipjs($text1, $text2, $text3, $text4, $text5, $text6, type_call); } function clicktocall_video() { $operation = $("#idcliente").val(); $pais_selected = document.getElementsByClassName("iti__selected-flag")[0].title.replace(/[^0-9]+/g, ""); $text2 = $pais_selected; $text2 += $("#text2").val(); document.getElementById("divLoad").style.display = ""; type_call = "call-audiovideo"; window.wvx_call_active = true; $text1 = $("#text1").val(); $text3 = $("#text3").val(); $text4 = $text4 == '' && typeof $text4 !== 'undefined' ? $("#text4").val() : $text4; $text5 = $("#text5").val(); $text6 = $("#text6").val(); $text1 = typeof $text1 !== 'undefined' ? $text1.replace(/(<([^>]+)>)/ig, "") : ''; $text2 = typeof $text2 !== 'undefined' ? $text2.replace(/(<([^>]+)>)/ig, "") : ''; $text3 = typeof $text3 !== 'undefined' ? $text3.replace(/(<([^>]+)>)/ig, "") : ''; $text4 = typeof $text4 !== 'undefined' ? $text4.replace(/(<([^>]+)>)/ig, "") : ''; $text5 = typeof $text5 !== 'undefined' ? $text5.replace(/(<([^>]+)>)/ig, "") : ''; $text6 = typeof $text6 !== 'undefined' ? $text6.replace(/(<([^>]+)>)/ig, "") : ''; conexion_sipjs($text1, $text2, $text3, $text4, $text5, $text6, type_call); } function callback() { // $pais_selected = document.getElementsByClassName("iti__selected-flag")[0].title.replace(/[^0-9]+/g, ""); type_call = "callback"; $text1 = $("#text1").val(); $text2 = $("#text2").val(); $text3 = $("#text3").val(); $text4 = $text4 == '' && typeof $text4 !== 'undefined' ? $("#text4").val() : $text4; $text5 = $("#text5").val(); $text6 = $("#text6").val(); $text1 = typeof $text1 !== 'undefined' ? $text1.replace(/(<([^>]+)>)/ig, "") : ''; $text2 = typeof $text2 !== 'undefined' ? $text2.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/ ]/gi, "") : ''; $text3 = typeof $text3 !== 'undefined' ? $text3.replace(/(<([^>]+)>)/ig, "") : ''; $text4 = typeof $text4 !== 'undefined' ? $text4.replace(/(<([^>]+)>)/ig, "") : ''; $text5 = typeof $text5 !== 'undefined' ? $text5.replace(/(<([^>]+)>)/ig, "") : ''; $text6 = typeof $text6 !== 'undefined' ? $text6.replace(/(<([^>]+)>)/ig, "") : ''; callback_register($text1, $text2, $text3, $text4, $text5, $text6, type_call); } function validate() { var email = $("#text3").val(); var tel = $("#text2").val(); var validatorTelefono = 0; var validatorEmail = 0; var validator = 0; var text1 = document.getElementById("text1"); var text2 = document.getElementById("text2"); var text3 = document.getElementById("text3"); var text4 = document.getElementById("text4"); var text5 = document.getElementById("text5"); var text6 = document.getElementById("text6"); var habeasData = document.getElementById("habeasData"); var habeasDataPass = false; if (text1.value != "") { text1.style.border = ""; validatorEmail = 1; } else { text1.focus(); text1.style.border = "rgba(255, 0, 0, 0.41) solid 1px"; return false; } if ($("#idcliente").val() == "konecta-electrolux-nola") { // Dar valor por defecto a tel y text2 tel = "123456789"; text2.value = "123456789"; } var minPhoneLen = (typeof window.phoneMinLength !== 'undefined' && window.phoneMinLength > 0) ? window.phoneMinLength : 7; if (tel.length >= minPhoneLen) { text2.style.border = ""; validatorTelefono = 1; } else { text2.focus(); text2.style.border = "rgba(255, 0, 0, 0.41) solid 1px"; validatorTelefono = 0; return false; } if (validateEmail(email)) { text3.style.border = ""; validatorEmail = 1; } else { text3.focus(); text3.style.border = "rgba(255, 0, 0, 0.41) solid 1px"; validatorEmail = 0; return false; } if ($("#habeasData").length > 0) { if ($("#habeasData").prop("checked")) { habeasData.style.setProperty('--coloree', '2px solid rgb(85 85 85)'); habeasData.style.setProperty('--shake', ''); habeasDataPass = true; } else { habeasData.style.setProperty('--shake', ''); habeasData.style.setProperty('--coloree', '2px solid rgb(255 0 0)'); habeasData.style.setProperty('--shake', 'shake 0.5s'); } } else { habeasDataPass = true; } if (validatorEmail == 1 && validatorTelefono == 1 && (habeasDataPass)) { document.getElementById("enviar_form_chat").disabled = true; if (!hasChannelOptions() || $("#container-options").attr("data-init")) { try { if (window.localStorage) { /* guardar formulario en localStorage */ if (text1 !== null) { window.localStorage.setItem('chat.wolkvox.form.text1', text1.value); } if (text2 !== null) { window.localStorage.setItem('chat.wolkvox.form.text2', text2.value); } if (text3 !== null) { window.localStorage.setItem('chat.wolkvox.form.text3', text3.value); } if (text4 !== null) { window.localStorage.setItem('chat.wolkvox.form.text4', text4.value); } if (text5 !== null) { window.localStorage.setItem('chat.wolkvox.form.text5', text5.value); } if (text6 !== null) { window.localStorage.setItem('chat.wolkvox.form.text6', text6.value); } /* end guardar formulario en localStorage */ } } catch (error) { } submit_form(); /*inicia chat*/ } else { if (!($("#container-options").attr("data-init"))) { try { if (window.localStorage) { /* guardar formulario en localStorage */ if (text1 !== null) { window.localStorage.setItem('chat.wolkvox.form.text1', text1.value); } if (text2 !== null) { window.localStorage.setItem('chat.wolkvox.form.text2', text2.value); } if (text3 !== null) { window.localStorage.setItem('chat.wolkvox.form.text3', text3.value); } if (text4 !== null) { window.localStorage.setItem('chat.wolkvox.form.text4', text4.value); } if (text5 !== null) { window.localStorage.setItem('chat.wolkvox.form.text5', text5.value); } if (text6 !== null) { window.localStorage.setItem('chat.wolkvox.form.text6', text6.value); } /* end guardar formulario en localStorage */ } } catch (error) { } mostrarButtons(); /*muestra botones*/ } } } return false; } var wvxUserChoseChat = false; function hasChannelOptions() { var $options = $("#container-options"); if (!$options.length) return false; return $options.find(".option-channel-btn").length > 0; } function mostrarButtons() { $("#container-options").attr("data-init", true); $("#container-options").show(); $(".containerChatForm").hide(); } function submit_form() { // data-init solo significa "ya se mostró el picker", no "el usuario eligió Chat". // El consentimiento es wvxUserChoseChat, que pone el clic en #start_chat_options. if (hasChannelOptions() && !wvxUserChoseChat) { mostrarButtons(); return; } $("#chat-input").prop('disabled', false); $idsession = $("#idsession").val(); $idcliente = $("#idcliente").val(); $skill = $("#skill").val(); // Cuando formInfo está activo se omite el formulario completamente: // se envían campos vacíos para que el backend no los valide. if (window.formInfo === true || window.formInfo == 1) { $text1 = ''; $text2 = ''; $text3 = ''; $text4 = ''; $text5 = ''; $text6 = ''; if (sendMsgType == 1) { chatbot($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill); } else if (sendMsgType == 2) { document.getElementById("chat_input_attach").setAttribute("multiple", ""); enrutamiento($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $id_config, $prefA); } return; } // Flujo normal con formulario $text1 = $("#text1").val(); $text2 = $("#text2").val(); $text3 = $("#text3").val(); $text4 = $("#text4").val(); $text5 = $("#text5").val(); $text6 = $("#text6").val(); $text1 = typeof $text1 !== 'undefined' ? $text1.replace(/(<([^>]+)>)/ig, "") : ''; $text2 = typeof $text2 !== 'undefined' ? $text2.replace(/\D/g, "") : ''; $text3 = typeof $text3 !== 'undefined' ? $text3.replace(/(<([^>]+)>)/ig, "") : ''; $text4 = typeof $text4 !== 'undefined' && $text4 != null ? $text4.replace(/(<([^>]+)>)/ig, "") : ''; $text5 = typeof $text5 !== 'undefined' && $text5 != null ? $text5.replace(/(<([^>]+)>)/ig, "") : ''; $text6 = typeof $text6 !== 'undefined' ? $text6.replace(/(<([^>]+)>)/ig, "") : ''; if (($text1.length > 0) && ($text2.length > 0) && ($text3.length > 0)) { if (sendMsgType == 1) { chatbot($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill); } else if (sendMsgType == 2) { document.getElementById("chat_input_attach").setAttribute("multiple", ""); enrutamiento($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $id_config, $prefA); } } } function enrutamiento($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $id_config, $prefA) { var datos = new Object(); datos.text1 = $text1; datos.text2 = $text2; datos.text3 = $text3; datos.text4 = $text4; datos.text5 = $text5; datos.idsession = $idsession; datos.idcliente = $idcliente; datos.skill = $skill; datos.id_config = $id_config; datos.pref = $prefA; datos.CI = chat_id; datos.operation = $("#idcliente").val(); $text6 = $text6.replace(/\n/g, "\\n"); datos.text6 = $text6; $.ajax({ url: "querys/users.php", data: datos, type: "POST", dataType: 'json', error: function () { document.getElementById("enviar_form_chat").disabled = false; }, success: function (data) { if (data.agente != "") { agente_asignado = data.agente; agente = data.agente; var msgSaludo = ''; recibir = setInterval(function () { recibir_msgs() }, 5000); $("#chat-input").prop('disabled', false); chat_id = data.ic; msgSaludo = data.msgDone + " " + agente_asignado; $("#chat-input").prop('disabled', false); } else { var msgSinAgent = data.msgFail; generate_messagebot(msgSinAgent); $("#chat-input").prop('disabled', true); } div1 = document.getElementById('containerChatLogs'); div2 = document.getElementById('containerChatForm'); remove = document.getElementById('close'); div1.style.display = ''; div2.style.display = 'none'; if (remove !== null) { remove.style.display = ''; } if (full == 0) { var minimizeChatElement = document.getElementById("minimizeChat"); var closeElement = document.getElementById("close"); var actionsHeaderElement = document.getElementById("actionsHeader"); if (device == "mobile") { if (minimizeChatElement) { minimizeChatElement.classList.add("pl-2"); } if (closeElement) { closeElement.classList.add("pl-3"); } } else { if (actionsHeaderElement) { actionsHeaderElement.classList.add("d-inline-flex"); } if (minimizeChatElement) { minimizeChatElement.classList.add("pl-2"); } if (closeElement) { closeElement.classList.add("pl-2"); } } var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].removeAttribute("style"); } } } }); } function chatbot($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $msg) { var datos = new Object(); var itiEl = document.getElementsByClassName("iti__selected-flag")[0]; $pais_selected = itiEl ? itiEl.title.replace(/[^0-9]+/g, "") : ""; datos.text1 = $text1; if (window.formInfo === true || window.formInfo == 1) { datos.text2 = ""; } else { datos.text2 = $pais_selected + $text2; } datos.text3 = $text3; datos.text4 = $text4; datos.text5 = $text5; datos.text6 = $text6; datos.idsession = $idsession; datos.idcliente = $idcliente; datos.skill = $skill; datos.msg = $msg; datos.urlOrigen = urlOrigen; datos.idConfig = idConfig; datos.operation = $("#idcliente").val(); $text6 = $text6.replace(/\n/g, "\\n"); $.ajax({ url: "querys/users_routing.php", data: datos, type: "POST", dataType: 'JSON', error: function () { lastRoutingReentrySkill = ''; document.getElementById("enviar_form_chat").disabled = false; }, success: function (data) { div1 = document.getElementById('containerChatLogs'); div2 = document.getElementById('containerChatForm'); remove = document.getElementById('close'); div1.style.display = ''; div2.style.display = 'none'; $("#container-options").hide(); if (remove !== null) { remove.style.display = ''; } if (chat != "full") { var minimizeChatElement = document.getElementById("minimizeChat"); var closeElement = document.getElementById("close"); var actionsHeaderElement = document.getElementById("actionsHeader"); if (device == "mobile") { if (minimizeChatElement) { minimizeChatElement.classList.add("ml-4"); } if (closeElement) { closeElement.classList.add("ml-3"); } } else { if (actionsHeaderElement) { actionsHeaderElement.classList.add("d-inline-flex"); } } var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].removeAttribute("style"); } } chat_id = data.id; attending = data.attending; var mshSendBot = " "; if (attending == false) { chatbotmsg($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, mshSendBot, context); } else { recibir_msgs(); } } }); } function starDatos() { if (window.formInfo === true || window.formInfo == 1) { // Con formInfo activo no se necesita rellenar campos: el backend no los valida si llegan vacíos if (desplegado == 0 || full == 1) { atstrt = 1; submit_form(); } else { if (!hasChannelOptions() || $("#container-options").attr("data-init")) { document.getElementById('containerChatLogs').style.display = ''; document.getElementById('containerChatForm').style.display = 'none'; $("#container-options").hide(); document.getElementById('close').style.display = ''; if (chat != "full") { var minimizeChatElement = document.getElementById("minimizeChat"); var closeElement = document.getElementById("close"); var actionsHeaderElement = document.getElementById("actionsHeader"); if (device == "mobile") { if (minimizeChatElement) { minimizeChatElement.classList.add("ml-4"); } if (closeElement) { closeElement.classList.add("ml-3"); } } else { if (actionsHeaderElement) { actionsHeaderElement.classList.add("d-inline-flex"); } } var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].removeAttribute("style"); } } } else { if (!($("#container-options").attr("data-init"))) { mostrarButtons(); } } } return; } if (txt1 != "") { $("#text1").val(atob(txt1)); $("#text2").val(atob(txt2)); $("#text3").val(atob(txt3)); $("#text4").val(atob(txt4)); $("#text5").val(atob(txt5)); $("#text6").val(atob(txt6)); $("#habeasData").prop("checked", true) atstrt = 1; if (desplegado == 0 || full == 1) { $('#enviar_form_chat').trigger("click"); } else { if (!hasChannelOptions() || $("#container-options").attr("data-init")) { document.getElementById('containerChatLogs').style.display = ''; document.getElementById('containerChatForm').style.display = 'none'; $("#container-options").hide(); document.getElementById('close').style.display = ''; if (chat != "full") { var minimizeChatElement = document.getElementById("minimizeChat"); var closeElement = document.getElementById("close"); var actionsHeaderElement = document.getElementById("actionsHeader"); if (device == "mobile") { if (minimizeChatElement) { minimizeChatElement.classList.add("ml-4"); } if (closeElement) { closeElement.classList.add("ml-3"); } } else { if (actionsHeaderElement) { actionsHeaderElement.classList.add("d-inline-flex"); } } var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].removeAttribute("style"); } } } else { if (!($("#container-options").attr("data-init"))) { mostrarButtons(); /*muestra botones*/ } } } } } $("#chat-submit").click(function (e) { e.preventDefault(); updateUserActivity(); // Registrar actividad del usuario var msg = $("#chat-input").val(); msg = DOMPurify.sanitize(msg); if (msg.trim() === '') { return false; } generate_message(msg, 'self'); var buttons = [ { name: 'Existing User', value: 'existing' }, { name: 'New User', value: 'new' }]; var msg_min = msg.toLowerCase(); var ln = window.navigator.language || navigator.browserLanguage; }); var mensajeBack = ''; /** * POLLER ESPECIALISTA: Para chat con agente y encuestas. Es el único que entiende el mensaje "|fin|". * Esta es la función de tu JS original, que funciona correctamente. */ function reenterRouting(encryptedSkill) { if (encryptedSkill === undefined || encryptedSkill === null || encryptedSkill === '' || encryptedSkill === 0 || encryptedSkill === '0') { return false; } if (lastRoutingReentrySkill === encryptedSkill) { return false; } $skill = encryptedSkill; $("#skill").val(encryptedSkill); sendMsgType = 1; if ($("#idcliente").val().indexOf("ipcom") !== -1) { return false; } lastRoutingReentrySkill = encryptedSkill; if (recibir) { clearInterval(recibir); recibir = ''; } var idsession = $("#idsession").val(); var idcliente = $("#idcliente").val(); var t1 = ''; var t2 = ''; var t3 = ''; var t4 = ''; var t5 = ''; var t6 = ''; if (!(window.formInfo === true || window.formInfo == 1)) { t1 = ($("#text1").val() || '').replace(/(<([^>]+)>)/ig, ""); t2 = ($("#text2").val() || '').replace(/\D/g, ""); t3 = ($("#text3").val() || '').replace(/(<([^>]+)>)/ig, ""); t4 = ($("#text4").val() || '').replace(/(<([^>]+)>)/ig, ""); t5 = ($("#text5").val() || '').replace(/(<([^>]+)>)/ig, ""); t6 = ($("#text6").val() || '').replace(/(<([^>]+)>)/ig, ""); } chatbot(t1, t2, t3, t4, t5, t6, idsession, idcliente, encryptedSkill); return true; } function recibir_msgs() { var datos = new Object(); datos.idmsg = idmsg; // Usa la variable `idmsg` original datos.ses = session; datos.idcliente = $("#idcliente").val(); datos.ic = chat_id; datos.skill = $skill; datos.operation = $("#idcliente").val(); // LOG DETALLADO: Parámetros enviados a recibir.php $.ajax({ dataType: 'json', url: "querys/recibir.php", data: datos, type: "POST", success: function (data) { // LOG DETALLADO: Respuesta de recibir.php sendMsgType = data.sendMsgType; var mensaje = ''; if (sendMsgType == 2) { lastRoutingReentrySkill = ''; } if (typeof data.SKILL !== "undefined" && sendMsgType == 1) { reenterRouting(data.SKILL); } if (typeof data.SKILL_queue !== "undefined") { if (sendMsgType == 2) { $skill = data.SKILL_queue; $("#skill").val(data.SKILL_queue); } } if (data.CHAT != null) { for (i = 0; i < data.CHAT.length; i++) { // --- INICIO DE LA MODIFICACIÓN --- idmsg = data.CHAT[i].id;//debe ir encriptado lastDisplayedMessageId = parseInt(idmsg) || lastDisplayedMessageId; // SOLUCIÓN: Mantener como número // --- FIN DE LA MODIFICACIÓN --- mensaje = data.CHAT[i].msg; auto = data.CHAT[i].auto; agente = data.CHAT[i].desde != "Survey" ? data.CHAT[i].desde : languages.web_nameSurvey; fecha = data.CHAT[i].fecha; // LOG DETALLADO: Cada mensaje individual if (mensaje != "|fin|") { generate_message(mensaje, 'user', auto, agente, ""); count++; newTitle = '(' + count + ') '; parent.postMessage("title" + newTitle, "*"); } else { clearInterval(recibir); $("#chat-input").prop('disabled', true); } } } if (typeof data.ROUTING !== "undefined" && data.ROUTING != "" && data.ROUTING != 0) { reenterRouting(data.ROUTING); } } }); } var mesok = new Array(12); mesok[0] = languages.web_mesok0; mesok[1] = languages.web_mesok1; mesok[2] = languages.web_mesok2; mesok[3] = languages.web_mesok3; mesok[4] = languages.web_mesok4; mesok[5] = languages.web_mesok5; mesok[6] = languages.web_mesok6; mesok[7] = languages.web_mesok7; mesok[8] = languages.web_mesok8; mesok[9] = languages.web_mesok9; mesok[10] = languages.web_mesok10; mesok[11] = languages.web_mesok11; function aplicarNegrita(str) { // Utilizamos una expresión regular para buscar palabras entre "**" var regex = /\*(.*?)\*/g; // Reemplazamos las coincidencias con la versión en negrita var resultado = str.replace(regex, '$1'); return resultado; } function generate_message(msg, type, auto, agent, attach, fecha) { var datos = new Object(); datos.msg = msg; datos.type = type; datos.auto = auto; datos.agent = agent; datos.attach = attach; datos.fecha = fecha; // Aplicar negrita al mensaje msg = aplicarNegrita(msg); // Corrección quirúrgica: Reparar íconos de adjuntos (propios o externos como el del Agente) if (msg) { // 1. Reparar íconos con src vacío if (msg.indexOf('src=""') !== -1) { if (msg.toLowerCase().indexOf('.xls') !== -1 || msg.toLowerCase().indexOf('.xlsx') !== -1) { msg = msg.replace('src=""', 'src="https://storage.googleapis.com/wvx-static/images_w/Chat_XLS.png"'); } else if (msg.toLowerCase().indexOf('.pdf') !== -1) { msg = msg.replace('src=""', 'src="https://storage.googleapis.com/wvx-static/images_w/Chat_PDF.png"'); } else if (msg.toLowerCase().indexOf('.csv') !== -1) { msg = msg.replace('src=""', 'src="https://storage.googleapis.com/wvx-static/images_w/Chat_CSV.png"'); } else if (msg.toLowerCase().indexOf('.doc') !== -1 || msg.toLowerCase().indexOf('.docx') !== -1) { msg = msg.replace('src=""', 'src="https://storage.googleapis.com/wvx-static/images_w/Chat_WORD.png"'); } } // 2. Reparar rutas relativas del agente que no existen en el widget (ej: /chat/agent/img/csv.png) if (msg.indexOf('/chat/agent/img/') !== -1) { msg = msg.replace(/\/chat\/agent\/img\/pdf\.png/gi, 'https://storage.googleapis.com/wvx-static/images_w/Chat_PDF.png'); msg = msg.replace(/\/chat\/agent\/img\/csv\.png/gi, 'https://storage.googleapis.com/wvx-static/images_w/Chat_CSV.png'); msg = msg.replace(/\/chat\/agent\/img\/(word|doc|docx)\.png/gi, 'https://storage.googleapis.com/wvx-static/images_w/Chat_WORD.png'); msg = msg.replace(/\/chat\/agent\/img\/(excel|xls|xlsx)\.png/gi, 'https://storage.googleapis.com/wvx-static/images_w/Chat_XLS.png'); } } msg = wvxNormalizeImageStyleInline(msg); // Si se proporciona una fecha, usarla, de lo contrario, usar la fecha actual var d; if (typeof fecha !== 'undefined' && fecha !== "") { // LOG DETALLADO: Información de timezone para mensajes normales // Convertir la fecha de entrada a un objeto Date let fechaEntrada = new Date(fecha); // Obtener el GMT local en minutos let gmtLocal = -new Date().getTimezoneOffset(); // Obtener el GMT de la fecha de entrada en minutos let gmtFechaEntrada = -fechaEntrada.getTimezoneOffset(); // Si los GMT son diferentes, ajustar la fecha al GMT local if (gmtFechaEntrada !== gmtLocal) { var fechaAntes = new Date(fechaEntrada); // Calcular la diferencia en minutos let diferencia = gmtLocal - gmtFechaEntrada; // Ajustar la fecha sumando la diferencia en minutos fechaEntrada.setMinutes(fechaEntrada.getMinutes() + diferencia); } // Asignar la fecha ajustada a la variable d d = new Date(fechaEntrada.getTime()); } else { // Si no hay fecha, usar la fecha y hora actuales d = new Date(); } // Obtener minutos y segundos formateados var min = d.getMinutes(); var mins = (min < 10) ? '0' + min : min; var seg = d.getSeconds(); var segs = (seg < 10) ? '0' + seg : seg; INDEX++; var str = ""; str += "
"; str += " "; // Avatar asesor: sin burbuja (hideMessageBubble o dispoMessage == 0) imagen siempre; nombre opcional. // Con burbujas: nunca imagen en el avatar (solo nombre encima de la burbuja si existe). if (type == 'user') { if (hideMessageBubble || dispoMessage == 0) { str += "
"; str += " "; if (nameAgent && nameAgent !== '') { str += " " + nameAgent + ""; } str += "
"; } } str += " <\/span>"; if (type == 'user') { try { audio.play(); } catch (excep) { } var advisorBgStyle = window.advisorBgColor ? 'background: rgb(' + window.advisorBgColor + ');' : ''; var advisorTextStyle = window.advisorTextColor ? 'color: rgb(' + window.advisorTextColor + ');' : ''; str += (hideMessageBubble || dispoMessage == 0) ? '
' : '
'; if (!(hideMessageBubble || dispoMessage == 0) && nameAgent && nameAgent !== '') { str += '' + nameAgent + ''; } if ( (msg.indexOf('http') != -1 || msg.indexOf('www') != -1 || msg.indexOf('.com') != -1) //Verifica si el mensaje contiene un link (http, www o .com) && (msg.indexOf('') == -1) //Verifica que el mensaje no tenga ya un link HTML () && (msg.indexOf('storage.googleapis.com/wvx-chat-attachment') == -1) //Excluye los links de archivos adjuntos almacenados en Google Cloud Storage ) { msg = linkify(msg); //Convierte texto con URLs en enlaces HTML clickeables if (msg.indexOf(agent) === -1) { if (auto == "a") { str += msg; clearInterval(recibir); $("#chat-input").prop('disabled', true); } else if (auto == "t") { str += msg; } else { // Si el agente NO es el chat_bot, añade el prefijo. De lo contrario, solo el mensaje. if (agent != 'chat_bot') { str += agent + ": " + msg; } else { str += msg; } } } else { str += msg; } } else if (msg.indexOf('http') === -1 || msg.indexOf('www') === -1 || msg.indexOf('.com') === -1 || msg.indexOf('') !== -1) { if (msg.indexOf(agent) === -1) { if (auto == "a") { str += msg; clearInterval(recibir); $("#chat-input").prop('disabled', true); } else if (auto == "t") { str += msg; } else { // Si el agente NO es el chat_bot, añade el prefijo. De lo contrario, solo el mensaje. if (agent != 'chat_bot') { str += agent + ": " + msg; } else { str += msg; } } } else { str += msg; } } str += "
"; str += (hideMessageBubble || dispoMessage == 0) ? "" : " " + d.getDate() + " " + mesok[d.getMonth()] + " " + d.getHours() + ":" + mins + ":" + segs + "<\/span>"; str += " <\/div>"; str += " <\/div>"; // str = str.replace(/<[^>]+>|(\b(?:onerror|onload|onclick|onmouseover|onmouseout|onfocus|onblur|onkeyup|onkeydown|onkeypress)\s*=\s*['"]?[^'"]*['"]?)/gi, ''); $(".chat-logs").append(str); $("#cm-msg-" + INDEX).hide().fadeIn(300); $(".chat-logs").stop().animate({ scrollTop: $(".chat-logs")[0].scrollHeight }, 1000); // Verificar si el mensaje contiene una etiqueta con id='autoRedirectSurvey' y hacer click automático // Defensa en profundidad: no auto-redirigir durante llamadas ni si "campana" viene vacía if (msg.indexOf("') !== -1) { msg = msg.replace(/(<([^>]+)>)/ig, ""); } var clientBgStyle = window.clientBgColor ? 'background: rgb(' + window.clientBgColor + ');' : ''; var clientTextStyle = window.clientTextColor ? 'color: rgb(' + window.clientTextColor + ');' : ''; str += (hideMessageBubble || dispoMessage == 0) ? `
${(imgUser && imgUser != null && imgUser != '') ? `` : ''} ${nameCustomer}
` : '
'; if (!(hideMessageBubble || dispoMessage == 0) && nameCustomer && nameCustomer !== '') { str += '' + nameCustomer + ''; } if(attach != null && attach != "") { var ext = msg.split('.').pop().toLowerCase(); var imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (imageExts.indexOf(ext) !== -1) { str += "
" + msg + ""; } else { var iconUrl = 'https://storage.googleapis.com/wvx-static/images_w/Chat_PDF.png'; if (ext === 'csv') iconUrl = 'https://storage.googleapis.com/wvx-static/images_w/Chat_CSV.png'; else if (ext === 'xls' || ext === 'xlsx') iconUrl = 'https://storage.googleapis.com/wvx-static/images_w/Chat_XLS.png'; else if (ext === 'doc' || ext === 'docx') iconUrl = 'https://storage.googleapis.com/wvx-static/images_w/Chat_WORD.png'; str += "
" + msg + "
Download"; if (ext === 'pdf') { str += " - View in another tab"; } str += "
"; } } else { str += msg; } str += "
"; if (!hideDateTime && !(hideMessageBubble || dispoMessage == 0)) { str += " " + d.getDate() + " " + mesok[d.getMonth()] + " " + d.getHours() + ":" + mins + ":" + segs + "<\/span>"; } else { str += " <\/span>"; } str += " <\/div>"; str += " <\/div>"; $(".chat-logs").append(str); $("#cm-msg-" + INDEX).hide().fadeIn(300); $("#chat-input").val(''); $(".chat-logs").stop().animate({ scrollTop: $(".chat-logs")[0].scrollHeight }, 1000); wvxBindChatImageLayoutFix($("#cm-msg-" + INDEX).find("img.chat-attachment-preview, img.imageStyle")); } } var msgrec = ''; $("#chat-circle").ready(function () { var gmt = -new Date().getTimezoneOffset() / 60; var session = session2; operacion = $("#idcliente").val(); $.ajax({ dataType: 'json', url: "querys/session.php", data: { session: session, operation: operacion, gmt: gmt }, type: "POST", success: function (data) { if (Object.keys(data).length > 1) { sendMsgType = data.sendMsgType if (sendMsgType == 2) { document.getElementById('skill').value = data[1].SKILL; } $skill = $("#skill").val(); $idsession = $("#idsession").val(); $idcliente = $("#idcliente").val(); $("#text1").val(data[1].TEXT1); $("#text2").val(data[1].TEXT2); $("#text3").val(data[1].TEXT3); $("#text4").val(data[1].TEXT4); $("#text5").val(data[1].TEXT5); $("#text6").val(data[1].TEXT6); $text1 = $("#text1").val(); $text2 = $("#text2").val(); $text3 = $("#text3").val(); $text4 = $text4 == '' && typeof $text4 !== 'undefined' ? $("#text4").val() : $text4; $text5 = $("#text5").val(); $text6 = $("#text6").val(); $text1 = typeof $text1 !== 'undefined' ? $text1.replace(/(<([^>]+)>)/ig, "") : ''; $text2 = typeof $text2 !== 'undefined' ? $text2.replace(/(<([^>]+)>)/ig, "") : ''; $text3 = typeof $text3 !== 'undefined' ? $text3.replace(/(<([^>]+)>)/ig, "") : ''; $text4 = typeof $text4 !== 'undefined' ? $text4.replace(/(<([^>]+)>)/ig, "") : ''; $text5 = typeof $text5 !== 'undefined' && $text5 != null ? $text5.replace(/(<([^>]+)>)/ig, "") : ''; $text6 = typeof $text6 !== 'undefined' && $text6 != null ? $text6.replace(/(<([^>]+)>)/ig, "") : ''; chat_id = data[0].IC; agente_asignado = data[0].AGENT; agente = data[0].AGENT; var lasAgent = ""; for (i = 1; i < Object.keys(data).length - 1; i++) { tipo_dato = data[i].TYPE; from_chat = data[i].FROM_CHAT; from = data[i].FROM; if (tipo_dato == 'CHAT') { // idmsg debe conservar el ID ENCRIPTADO del ultimo mensaje del // historial. recibir.php lo desencripta internamente para filtrar // mensajes posteriores (WHERE id > N). No aplicar parseInt aqui // porque data[i].ID es un string encriptado (no numerico) y el // resultado seria NaN, lo que provocaria que recibir.php trajera // TODO el historial en cada poll y lo repintara. idmsg = data[i].ID; mensaje = data[i].MSG; if (from_chat == 'AGENT') { if (from == "chat_bot") { generate_messagebot(mensaje, "", "", data[i].FECHA); } else { generate_message(mensaje, "user", 0, agente, "", data[i].FECHA); } lasAgent = from; } else { generate_message(mensaje, "self", "", "", "", data[i].FECHA); } } } if (desplegado == 0 || full == 1) { iframeResize(heightFrameChat, widthFrameChat, "0"); } atstrt = 1; startedChat = 1; if (lasAgent != "chat_bot") { recibir = setInterval(function () { recibir_msgs() }, 5000); } $("#containerChatForm").css("display", "none"); $("#containerChatLogs").css("display", ""); if ((desplegado == 0 || full == 1) && !$(".chat-box").is(":visible")) { console.log("[Chat abierto4] comportCargar (BD):", typeof window.comportCargar !== "undefined" ? window.comportCargar : "(no definido)"); $("#chat-circle").toggle('scale'); $(".chat-box").toggle('scale'); } if (device == "mobile") { var minimizeChatElement = document.getElementById("minimizeChat"); if (minimizeChatElement) { minimizeChatElement.classList.add("ml-4"); } } else { var actionsHeaderElement = document.getElementById("actionsHeader"); if (actionsHeaderElement) { actionsHeaderElement.classList.add("d-inline-flex"); } } $(".chat-logs").stop().animate({ scrollTop: $(".chat-logs")[0].scrollHeight }, 2200); remove = document.getElementById('close'); if (remove !== null) { remove.style.display = ''; } if (full == 0) { var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].removeAttribute("style"); } } if (txt1 != "") { atstrt = 1; } } else { starDatos(); } } /* fin consultar azure maps */ }); if (desplegado == 0 || full == 1) { cargarPais(true); } $("#chat-circle").click(function () { console.log("[Chat abierto5] comportCargar (BD):", typeof window.comportCargar !== "undefined" ? window.comportCargar : "(no definido)"); iframeResize(heightFrameChat, widthFrameChat, "0"); $("#chat-circle").hide(); $(".chat-box").toggle('fast', function () { }); cargarPais(false); if (desplegado == 1 && startedChat == 0 && (atstrt == 1 || window.formInfo == 1 || window.formInfo === true)) { atstrt = 1; startedChat = 1; submit_form(); } }) if (typeof window.comportCargar !== 'undefined' && window.comportCargar == 0 && desplegado != 0 && (window.formInfo === true || window.formInfo == 1)) { setTimeout(function () { $("#chat-circle").trigger('click'); }, 500); } $(".chat-box-toggle").click(function () { $("#chat-circle").toggle('scale'); $(".chat-box").toggle('fast', function () { iframeResize(heightFrameWidget, widthFrameWidget, "1%"); }); }) $(".chat-box-close").click(function () { var popup = document.getElementById("popUpChat_chat_micro_finalizar"); popup.classList.toggle("show"); }) $("#noFinalizarChat").click(function () { var popup = document.getElementById("popUpChat_chat_micro_finalizar"); popup.classList.toggle("show"); }) $("#finalizarChat").click(function () { $session = session; $.ajax({ url: "querys/close.php", data: { ci: chat_id, // ci: '123456789', operation: $("#idcliente").val(), skill: $skill_bd, }, type: "POST", dataType: "json", complete: function (data) { cargarDatosLocalStorage("closed"); if (!$("#rememberme").prop("checked")) { localStorage.clear(); } // Recargar iframe para obtener nueva sesión PHP y evitar pantalla en blanco al iniciar otro chat window.setTimeout(function () { window.location.reload(); }, 100); } }); sendMsgType = 1; if (atstrt == 1) { $("#text1").val(''); $("#text2").val(''); $("#text3").val(''); $("#text4").val(''); $("#text5").val(''); $("#text6").val(''); document.getElementById('skill').value = $skill_bd; document.getElementById('chat-logs').innerHTML = ''; document.getElementById("enviar_form_chat").disabled = false; var div3 = document.getElementById('containerChatLogs'); var div4 = document.getElementById('containerChatForm'); div3.style.display = 'none'; div4.style.display = ''; remove = document.getElementById('close'); if (remove !== null) { remove.style.display = 'none'; } if (full == 0) { var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].setAttribute("style", "margin-right: 6px;"); } } var popup = document.getElementById("popUpChat_chat_micro_finalizar"); popup.classList.toggle("show"); var survey = document.getElementById('surveyipdial'); var input = document.getElementById('inputchatview'); input.style.display = ''; if ($("#formulario_chat_ipdialbox").css("visibility") == "hidden") { $("#formulario_chat_ipdialbox").css("visibility", "visible"); } clearInterval(recibir); $("#container-options").removeAttr("data-init"); wvxUserChoseChat = false; if (count > 0) { count = 0; newTitle = ""; parent.postMessage("title" + newTitle, "*"); } } else { // localStorage.clear(); // sessionStorage.clear(); $("#text1").val(''); $("#text2").val(''); $("#text3").val(''); $("#text4").val(''); $("#text5").val(''); $("#text6").val(''); document.getElementById('skill').value = $skill_bd; document.getElementById('chat-logs').innerHTML = ''; document.getElementById("enviar_form_chat").disabled = false; var div3 = document.getElementById('containerChatLogs'); var div4 = document.getElementById('containerChatForm'); div3.style.display = 'none'; div4.style.display = ''; remove = document.getElementById('close'); if (remove !== null) { remove.style.display = 'none'; } if (full == 0) { var chatBoxToggle = document.getElementsByClassName('chat-box-toggle'); if (chatBoxToggle.length > 0) { chatBoxToggle[0].setAttribute("style", "margin-right: 6px;"); } } var popup = document.getElementById("popUpChat_chat_micro_finalizar"); popup.classList.toggle("show"); var survey = document.getElementById('surveyipdial'); var input = document.getElementById('inputchatview'); input.style.display = ''; if ($("#formulario_chat_ipdialbox").css("visibility") == "hidden") { $("#formulario_chat_ipdialbox").css("visibility", "visible"); } clearInterval(recibir); $("#container-options").removeAttr("data-init"); wvxUserChoseChat = false; if (count > 0) { count = 0; newTitle = ""; parent.postMessage("title" + newTitle, "*"); } } if (full == 0) { $("#chat-circle").toggle('scale'); $(".chat-box").toggle('fast', function () { iframeResize(heightFrameWidget, widthFrameWidget, "1%"); }); } }); }) function cargarPais(init) { /* consultar azure maps */ var input = document.querySelector("#text2"); var inputsChat = document.querySelector("#inputsFormChat"); var flag_select = document.getElementsByClassName("iti__selected-flag"); var country_code_localStorage; var user_ip_localStorage; var text2_localStorage; if (flag_select.length == 0) { iti = window.intlTelInput(input, { dropdownContainer: inputsChat, initialCountry: "us" }); try { /* ingresa al input el telefono guardado en localstorage */ if ((text2_localStorage = window.localStorage.getItem('chat.wolkvox.form.text2'))) { iti.setNumber(text2_localStorage); } /* validar que hay datos del pais en localStorage */ if (window.localStorage.getItem('chat.wolkvox.countryCode') !== undefined && window.localStorage.getItem('chat.wolkvox.countryCode')) { country_code_localStorage = window.localStorage.getItem('chat.wolkvox.countryCode'); } /* validar que hay datos de la ip en localStorage */ if (window.localStorage.getItem('chat.wolkvox.user_ip') !== undefined && window.localStorage.getItem('chat.wolkvox.user_ip')) { user_ip_localStorage = window.localStorage.getItem('chat.wolkvox.user_ip'); } var isoCountry = function (code) { code = String(code || '').trim().toLowerCase(); if (code === '' || code === '-') { return 'us'; } return /^[a-z]{2}$/.test(code) ? code : 'us'; }; /* si la ip actual es igual al localStorage y el ISO es válido */ if (user_ip == user_ip_localStorage && country_code_localStorage) { iti.setCountry(isoCountry(country_code_localStorage)); } else { // Se envia el init en true cuando se despliegue automaticamente el chat, y se envia false cuando se despliega por el boton $.ajax({ url: '../chat/gp.php', type: 'POST', dataType: 'json', data: { 'attr': btoa(user_ip) + "|" + btoa(base_url), 'operation': $("#idcliente").val(), 'init': init } }).done(function (result) { var country_code_bd = (result && !result.error && result.countryRegion && result.countryRegion.isoCode) ? isoCountry(result.countryRegion.isoCode) : ''; if (/^[a-z]{2}$/.test(country_code_bd) && country_code_bd !== '-') { if (window.localStorage) { window.localStorage.setItem('chat.wolkvox.countryCode', country_code_bd); window.localStorage.setItem('chat.wolkvox.user_ip', user_ip); } iti.setCountry(country_code_bd); } else { iti.setCountry('us'); } }).fail(function () { iti.setCountry('us'); }); } } catch (error) { iti.setCountry('us'); } } /* fin consultar azure maps */ } if (desplegado == 0 || full == 1) { cargarPais(true); } $("#okAlertPopup").click(function () { document.getElementById("popUpChat_chat_micro_alert").classList.toggle("show"); document.getElementById("popUpChat_chat_micro_alert_txt").innerHTML = ""; colgar(); }) $("#chat-input").click(function () { updateUserActivity(); // Registrar actividad del usuario if (count > 0) { count = 0; newTitle = ""; parent.postMessage("title" + newTitle, "*"); } }) /*})*/ /** * Función principal para enviar mensajes al BOT. * Orquesta el uso del poller rápido y la transición al poller de agente. */ function chatbotmsg(text1, text2, text3, text4, text5, text6, idsession, idcliente, skill, msg, context, chat_id) { msg = DOMPurify.sanitize(msg); // Verificar si el selector de país existe antes de usarlo var paisElement = document.getElementsByClassName("iti__selected-flag")[0]; $pais_selected = paisElement ? paisElement.title.replace(/[^0-9]+/g, "") : ""; var datos = new Object(); datos.text1 = text1; datos.text2 = $pais_selected + $text2; datos.text3 = text3; datos.text4 = text4; datos.text5 = text5; datos.text6 = text6; datos.idsession = idsession; datos.idcliente = idcliente; datos.skill = skill; datos.msg = msg; datos.context = context; datos.operation = $("#idcliente").val(); datos.ic = chat_id; // 1. Detenemos CUALQUIER poller que estuviera activo para empezar un ciclo limpio. if (pollerInterval) { // LOG: Verificar cuándo se detiene el poller clearInterval(pollerInterval); pollerInterval = null; } if (recibir) { clearInterval(recibir); recibir = ''; } // 2. SOLUCIÓN CRÍTICA: Sincronizar lastDisplayedMessageId var currentLastId = parseInt(lastDisplayedMessageId) || 0; // 3. Iniciamos el POLLER INTELIGENTE para esta interacción con el bot. // IMPORTANTE: Se ejecuta para todos los mensajes (incluidos vacíos) para capturar // mensajes que anteceden a timers. El sistema inteligente ajusta la frecuencia automáticamente. startIntelligentPoller(idsession, idcliente); // 4. Hacemos la llamada principal a msgbot.php $.ajax({ url: "querys/msgbot.php", cache: false, data: datos, type: "POST", dataType: 'json', success: function (data) { // 5. ACTUALIZAR ACTIVIDAD - El poller inteligente continuará según la lógica adaptativa // Ya no detenemos automáticamente el poller, dejamos que el sistema inteligente decida updateUserActivity(); // Tus asignaciones de variables como IDCUST, PREF, etc. sendMsgType = data.sendMsgType; if (typeof data.IDCUST !== "undefined") { if (data.IDCUST != '') { $text4 = data.IDCUST; } } if (typeof data.PREF !== "undefined") { if (data.PREF != '') { $prefA = data.PREF; } } if (typeof data.cust_name !== "undefined") { var previousName = nameCustomer; nameCustomer = data.cust_name || "Usuario"; } if (typeof data.maxid !== "undefined" && data.maxid) { idmsg = data.maxid; // SOLUCIÓN: También actualizar lastDisplayedMessageId cuando se actualiza maxid var maxIdAsNumber = parseInt(data.maxid) || lastDisplayedMessageId; if (maxIdAsNumber > (parseInt(lastDisplayedMessageId) || 0)) { lastDisplayedMessageId = maxIdAsNumber; } } if (typeof data.cust_name !== "undefined") { var clientCfg = window.clientNameConfig || { tipo: 'nombre_cliente' }; if (clientCfg.tipo === 'sin_nombre') { nameCustomer = ''; } else if (clientCfg.tipo === 'personalizado') { nameCustomer = clientCfg.nombre || ''; } else { nameCustomer = data.cust_name != '' ? data.cust_name : "Usuario"; } } if (typeof data.SKILL !== "undefined") { reenterRouting(data.SKILL); return; } // --- LÓGICA DE RENDERIZADO CENTRALIZADA --- // 6. Renderizamos los mensajes de CONTINUE_MSGS, si existen. // Estos son los mensajes que el bot envía en secuencia (ej. la encuesta). if (typeof data.CONTINUE_MSGS !== "undefined" && data.CONTINUE_MSGS.length > 0) { data.CONTINUE_MSGS.forEach(function (msg) { var currentLastId = parseInt(lastDisplayedMessageId) || 0; var msgId = parseInt(msg.id); // SOLUCIÓN CRÍTICA: Verificar tanto por ID como por si ya fue procesado recientemente var wasRecentlyProcessed = recentlyProcessedMessages.includes(msgId); var isGreaterThanLast = msgId > currentLastId; // SOLUCIÓN CRÍTICA: Verificar contenido duplicado independiente del ID var messageContent = (msg.msg || '').trim(); var isDuplicateContent = recentMessageContents.some(function (content) { return content === messageContent; }); if (isGreaterThanLast && !wasRecentlyProcessed && !isDuplicateContent) { // Pasamos la información COMPLETA a la función de renderizado generate_messagebot(msg.msg, msg.attach, '', msg.time); lastDisplayedMessageId = msgId; // SOLUCIÓN: Mantener como número // SOLUCIÓN CRÍTICA: Marcar este mensaje como procesado recientemente recentlyProcessedMessages.push(msgId); recentMessageContents.push(messageContent); // SINCRONIZACIÓN CRÍTICA: También agregar a los arrays del poller para evitar duplicados cruzados pollerProcessedMessages.push(msgId); pollerMessageContents.push(messageContent); } }); } // 7. Renderizamos el REPLY final (típicamente para flujos sin continue_chatbot o mensajes de encolamiento) var replyBot = data.REPLY || ""; if (replyBot) { // Solo si hay un REPLY // Evitamos duplicar si el último mensaje de CONTINUE_MSGS es el mismo var lastContinueMsg = (data.CONTINUE_MSGS && data.CONTINUE_MSGS.length > 0) ? data.CONTINUE_MSGS[data.CONTINUE_MSGS.length - 1].msg.split('||BUTTONS||')[0].trim() : null; if (replyBot.trim() !== lastContinueMsg) { generate_messagebot(data.REPLY, data.ATTACH, data.BUTTONS, new Date().toISOString(), data.BUTTON_URL_URL, data.BUTTON_URL_TEXT); if (data.REPLY_ID) { lastDisplayedMessageId = parseInt(data.REPLY_ID) || lastDisplayedMessageId; // SOLUCIÓN: Convertir a número } } } // 7.1 NUEVO: Procesar AUDIO_URL si el bot responde con audio en lugar de texto if (data.AUDIO_URL && !replyBot) { // El bot respondió con audio, renderizar el audio generate_messagebot("", data.AUDIO_URL, data.BUTTONS, new Date().toISOString(), data.BUTTON_URL_URL, data.BUTTON_URL_TEXT); if (data.REPLY_ID) { lastDisplayedMessageId = parseInt(data.REPLY_ID) || lastDisplayedMessageId; } } // 8. Decidimos qué hacer a continuación: ¿Seguimos con el bot o pasamos al agente? if (data.paso_agente === "yes") { recibir = setInterval(function () { recibir_msgs(); }, 5000); } }, error: function () { // SOLUCIÓN DUPLICADOS: Si la llamada principal falla, también detenemos el poller rápido. if (pollerInterval) { // LOG: Verificar cuándo se detiene el poller clearInterval(pollerInterval); pollerInterval = null; } } }); } /** * POLLER RÁPIDO: Solo para mensajes de texto simples del bot mientras hay un timer. */ // Variables globales para rastrear mensajes procesados recientemente // SEPARAMOS los arrays para evitar conflictos entre msgbot.php y poller var recentlyProcessedMessages = []; // Para mensajes de msgbot.php (CONTINUE_MSGS) var recentMessageContents = []; // Para detectar contenido duplicado de msgbot.php var pollerProcessedMessages = []; // Para mensajes del poller (check_new_messages.php) var pollerMessageContents = []; // Para detectar contenido duplicado del poller function pollForNewMessages(sessionId, operationId) { // SOLUCIÓN DUPLICADOS: Verificar que el poller sigue activo antes de ejecutar if (!pollerInterval) { return; // Salir si el poller ya fue detenido } // SOLUCIÓN: Normalizar lastDisplayedMessageId a número para comparación consistente var currentLastId = parseInt(lastDisplayedMessageId) || 0; $.ajax({ url: 'querys/check_new_messages.php', type: 'POST', dataType: 'json', data: { session: sessionId, operation: operationId, last_id: currentLastId // Enviar como número }, success: function (response) { // SOLUCIÓN CRÍTICA: Verificar de nuevo si el poller sigue activo al recibir la respuesta if (!pollerInterval) { return; // Salir si el poller fue detenido mientras se ejecutaba el AJAX } var messages = response.messages || []; // ACTUALIZAR ESTADO DE MENSAJES PENDIENTES pollerConfig.hasPendingMessages = response.has_pending || false; if (messages.length > 0) { messages.forEach(function (msg) { var msgId = parseInt(msg.id); // NUEVA VERIFICACIÓN: Comprobar si el mensaje fue procesado recientemente POR EL POLLER var wasRecentlyProcessed = pollerProcessedMessages.includes(msgId); // SOLUCIÓN CRÍTICA: Verificar contenido duplicado independiente del ID (USANDO ARRAYS DEL POLLER) var messageContent = (msg.msg || '').trim(); var isDuplicateContent = pollerMessageContents.some(function (content) { return content === messageContent; }); // Lógica estándar: Solo procesar si no es duplicado Y no fue procesado recientemente var shouldProcess = msgId > currentLastId && !wasRecentlyProcessed && !isDuplicateContent; if (shouldProcess) { // SOLUCIÓN CRÍTICA: Actualizar lastDisplayedMessageId INMEDIATAMENTE antes del renderizado // para prevenir que el mismo mensaje sea procesado en iteraciones concurrentes lastDisplayedMessageId = msgId; currentLastId = msgId; // Actualizar también la variable local para siguientes iteraciones generate_messagebot(msg.msg, msg.attach_url, '', msg.time); // Actualizar el último mensaje procesado lastDisplayedMessageId = msgId; // Agregar a la lista de procesados recientemente DEL POLLER pollerProcessedMessages.push(msgId); pollerMessageContents.push(messageContent); // SINCRONIZACIÓN CRÍTICA: También agregar a los arrays de msgbot para evitar duplicados cruzados recentlyProcessedMessages.push(msgId); recentMessageContents.push(messageContent); } else { } }); } else { } } }); } /** * Función de renderizado. * Se asegura de separar el texto de los botones si vienen juntos. */ function generate_messagebot(msg = null, attach = null, buttons = null, fecha, urlButton = '', textButton = '') { // Lógica para separar "texto||BUTTONS||botones" if (msg && msg.includes('||BUTTONS||')) { const partes = msg.split('||BUTTONS||'); msg = partes[0].trim(); // Si no nos pasaron botones explícitamente, usamos los del mensaje if (!buttons) { buttons = partes[1].trim(); } } if (!msg && !attach && !buttons) { return; // No renderizar si todo está vacío } // ====================> FIN DEL AJUSTE <==================== msg = DOMPurify.sanitize(msg); agente = ""; var effectiveNameBot = (typeof window.nameBot !== 'undefined') ? window.nameBot : nameAgent; // Aplicar negrita si msg no es null msg = msg != null ? aplicarNegrita(msg) : null; // Reemplazar saltos de línea por espacios para evitar el problema msg = msg.replace(/\r?\n/g, '
'); msg = linkify(msg); msg = wvxNormalizeImageStyleInline(msg); // Si REPLY ya trae el HTML de sendAttach (…), no repetir la vista previa por ATTACH (misma imagen dos veces). var attachRender = attach; if (attachRender && msg && typeof msg === 'string' && isImageAttachmentUrl(attachRender) && //i.test(msg)) { attachRender = null; } // Si no se proporciona una fecha, usamos la fecha actual var d; if (typeof fecha !== 'undefined' && fecha !== "") { // CORRECCIÓN CRÍTICA: Forzar que JavaScript interprete la fecha como UTC // Si la fecha no tiene timezone info, agregarle 'Z' para indicar UTC let fechaCorregida = fecha; if (typeof fecha === 'string' && !fecha.includes('T') && !fecha.includes('Z')) { fechaCorregida = fecha.replace(' ', 'T') + 'Z'; } // Convertir la fecha corregida a un objeto Date let fechaEntrada = new Date(fechaCorregida); // Obtener el GMT local en minutos (MISMO CÁLCULO que generate_message) let gmtLocal = -new Date().getTimezoneOffset(); // Obtener el GMT de la fecha de entrada en minutos let gmtFechaEntrada = -fechaEntrada.getTimezoneOffset(); // Si los GMT son diferentes, ajustar la fecha al GMT local (MISMO CÁLCULO que generate_message) if (gmtFechaEntrada !== gmtLocal) { var fechaAntes = new Date(fechaEntrada); // Calcular la diferencia en minutos let diferencia = gmtLocal - gmtFechaEntrada; // Ajustar la fecha sumando la diferencia en minutos fechaEntrada.setMinutes(fechaEntrada.getMinutes() + diferencia); } // Asignar la fecha ajustada a la variable d d = new Date(fechaEntrada.getTime()); } else { d = new Date(); } var min = d.getMinutes(); var mins = (min < 10) ? '0' + min : min; var seg = d.getSeconds(); var segs = (seg < 10) ? '0' + seg : seg; var divBlockMessage = "cm-msg-" + INDEX; if (document.getElementById(divBlockMessage)) { var elemDiv = document.getElementById(divBlockMessage); elemDiv.style.pointerEvents = 'none'; if (elemDiv.children[1].children[0].tagName == "AUDIO") { elemDiv.style.pointerEvents = 'auto'; } } INDEX++; var str = ""; str += "
"; str += " "; if (hideMessageBubble || dispoMessage == 0) { str += ` `; if (effectiveNameBot && effectiveNameBot !== '') { str += `${effectiveNameBot}`; } } str += " "; try { audio.play(); } catch (excep) { } var advisorBgStyleBot = window.advisorBgColor ? 'background: rgb(' + window.advisorBgColor + ');' : ''; var advisorTextStyleBot = window.advisorTextColor ? 'color: rgb(' + window.advisorTextColor + ');' : ''; var topOffsetBot = (effectiveNameBot && effectiveNameBot !== '') ? '-16px' : '-28px'; str += (hideMessageBubble || dispoMessage == 0) ? `
` : `
`; if (!(hideMessageBubble || dispoMessage == 0) && effectiveNameBot && effectiveNameBot !== '') { str += `${effectiveNameBot}`; } if (attachRender == "" || attachRender == null) { str += ""; } else if (validateExtensionAudio(attachRender) || validateBase64Audio(attachRender)) { str += ""; } else { var attachStyle = 'width: 100%; display: block;'; if (msg && msg.trim() !== '') { attachStyle += ' margin-bottom: 8px;'; } var fileExt = attachRender.split('.').pop().toLowerCase().split('?')[0]; var isImage = isImageAttachmentUrl(attachRender); if (isImage) { str += ""; } else { var iconUrl = ""; var fileName = attachRender.split('/').pop().split('?')[0]; if (fileExt == 'pdf') { iconUrl = "https://storage.googleapis.com/wvx-static/images_w/Chat_PDF.png"; } else if (fileExt == 'csv') { iconUrl = "https://storage.googleapis.com/wvx-static/images_w/Chat_CSV.png"; } else if (fileExt == 'xls' || fileExt == 'xlsx') { iconUrl = "https://storage.googleapis.com/wvx-static/images_w/Chat_XLS.png"; } else if (fileExt == 'doc' || fileExt == 'docx') { iconUrl = "https://storage.googleapis.com/wvx-static/images_w/Chat_WORD.png"; } str += "
"; str += ""; str += "" + fileName + ""; str += "
"; } } str += msg; // Se inserta el mensaje limpio sin saltos de línea innecesarios var hasUrlButton = false; if (urlButton != '' && textButton != '') { // Botón de URL visualmente igual a los quick replies, pero abre en nueva pestaña var messageTimestamp = new Date(fecha).getTime(); var urlBtnId = 'btn_url_' + messageTimestamp; str += "

"; str += ""; hasUrlButton = true; } if (buttons == "" || buttons == null || buttons === undefined) { // No buttons } else { if (!hasUrlButton) { str += "

"; } const botonesArray = buttons.split(","); botonesArray.forEach((btns, index) => { // SOLUCIÓN: Usar timestamp del mensaje para IDs consistentes y persistencia var messageTimestamp = new Date(fecha).getTime(); var buttonId = 'btn_' + messageTimestamp + '_' + btns.trim().replace(/\s+/g, '_'); var isSelected = localStorage.getItem('button_selected_' + buttonId) === 'true'; var selectedClass = isSelected ? ' selected' : ''; var convBgSelected = (window.btnConvBgColor && window.btnConvBgColor !== '') ? window.btnConvBgColor : null; var convTextSelected = (window.btnConvTextColor && window.btnConvTextColor !== '') ? window.btnConvTextColor : null; var selectedStyle = isSelected ? (convBgSelected && convTextSelected ? 'background-color: ' + convBgSelected + '; color: ' + convTextSelected + ';' : 'background-color: rgb(var(--codigocolor)); color: rgb(var(--colortexto));') : ''; const htmlBoton = ""; str += htmlBoton; }); } if (!hideDateTime) { str += " " + d.getDate() + " " + mesok[d.getMonth()] + " " + d.getHours() + ":" + mins + ":" + segs + "<\/span>"; } else { str += " <\/span>"; } str += "
"; str += "
"; $(".chat-logs").append(str); $("#cm-msg-" + INDEX).hide().fadeIn(300); wvxBindChatImageLayoutFix($("#cm-msg-" + INDEX).find("img.chat-attachment-preview, img.imageStyle")); // SOLUCIÓN: Agregar eventos click para persistencia de botones if (buttons != "" && buttons != null) { buttons.split(",").forEach((btns) => { // SOLUCIÓN: Usar el mismo timestamp para encontrar el ID correcto var messageTimestamp = new Date(fecha).getTime(); var buttonId = 'btn_' + messageTimestamp + '_' + btns.trim().replace(/\s+/g, '_'); var buttonElement = document.getElementById(buttonId); if (buttonElement) { buttonElement.addEventListener('click', function () { // Cambiar estado del botón var isCurrentlySelected = this.classList.contains('selected'); if (isCurrentlySelected) { // Deseleccionar this.classList.remove('selected'); localStorage.setItem('button_selected_' + buttonId, 'false'); } else { // Seleccionar this.classList.add('selected'); localStorage.setItem('button_selected_' + buttonId, 'true'); } }); } }); } $(".chat-logs").stop().animate({ scrollTop: $(".chat-logs")[0].scrollHeight }, 1000); // SOLUCIÓN: Verificar persistencia en tiempo real y aplicar estilos if (buttons != "" && buttons != null) { // Esperar un momento para que el DOM esté listo setTimeout(function () { buttons.split(",").forEach((btns, index) => { var messageTimestamp = new Date(fecha).getTime(); var buttonId = 'btn_' + messageTimestamp + '_' + btns.trim().replace(/\s+/g, '_'); var localStorageKey = 'button_selected_' + buttonId; var localStorageValue = localStorage.getItem(localStorageKey); // Buscar el botón en el DOM y aplicar persistencia var buttonElement = document.getElementById(buttonId); if (buttonElement && localStorageValue === 'true') { buttonElement.classList.add('selected'); buttonElement.style.backgroundColor = '#4CAF50'; buttonElement.style.color = 'white'; } }); }, 100); // Pequeño delay para asegurar que el DOM esté listo } // SOLUCIÓN: Verificar persistencia global de TODOS los botones existentes setTimeout(function () { // Llamar a la función global de restauración restoreButtonPersistence(); }, 200); // Delay mayor para asegurar que todos los mensajes estén renderizados // Verificar si el mensaje contiene una etiqueta con id='autoRedirectSurvey' y hacer click automático // Defensa en profundidad: no auto-redirigir durante llamadas ni si "campana" viene vacía if (msg && msg.indexOf("$1'); replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim; replacedText = replacedText.replace(replacePattern2, '$1$2'); replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim; replacedText = replacedText.replace(replacePattern3, '$1'); return replacedText; } function NOW() { var date = new Date(); var aaaa = date.getFullYear(); var gg = date.getDate(); var mm = (date.getMonth() + 1); if (gg < 10) gg = "0" + gg; if (mm < 10) mm = "0" + mm; var cur_day = aaaa + "-" + mm + "-" + gg; var hours = date.getHours() var minutes = date.getMinutes() var seconds = date.getSeconds(); if (hours < 10) hours = "0" + hours; if (minutes < 10) minutes = "0" + minutes; if (seconds < 10) seconds = "0" + seconds; return cur_day + " " + hours + ":" + minutes + ":" + seconds; } var startTime; var timerInterval; var animationId; function startRecording() { navigator.mediaDevices.getUserMedia({ audio: true }) .then(function (stream) { mediaRecorder = new MediaRecorder(stream); mediaRecorder.start(); startTime = new Date(); // Evento que se dispara cada vez que se graba un fragmento de audio mediaRecorder.addEventListener('dataavailable', function (e) { chunks.push(e.data); }); $('#divWaveAudio').css('display', 'inline-block'); resetAnimation(); var AudioContext = (window.AudioContext || window.webkitAudioContext) var audioContent = new AudioContext(); var streamSource = audioContent.createMediaStreamSource(stream); obj.analyser = audioContent.createAnalyser(); streamSource.connect(obj.analyser); obj.analyser.fftSize = 512; obj.frequencyArray = new Float32Array(obj.analyser.fftSize); init() loop() timerInterval = setInterval(updateTimer, 1000); updateTimer(); }) .catch(function (err) { $('#record-submit').show() $('#stop-record-submit').hide() $('#cancel-record-submit').hide() $('#divWaveAudio').hide() $('#custom-file-upload').show() $('#divInputChat').show() }); // Actualizar la visualización de los botones $('#record-submit').hide() $('#custom-file-upload').hide() $('#divInputChat').hide() $('#stop-record-submit').css('display', 'inline-block'); $('#cancel-record-submit').css('display', 'inline-block'); } function stopRecording() { mediaRecorder.stop(); // Actualizar la visualización de los botones $('#record-submit').show() $('#stop-record-submit').hide() $('#cancel-record-submit').hide() $('#divWaveAudio').hide() $('#custom-file-upload').show() $('#divInputChat').show() resetAnimation(); // Crear el archivo WAV con los fragmentos grabados mediaRecorder.addEventListener('stop', function () { const blob = new Blob(chunks, { type: 'audio/wav' }); const audioUrl = URL.createObjectURL(blob); const audio = new Audio(audioUrl); blobToBase64(blob) $("#record-submit").fadeOut(1, function () { $("#loading-record").fadeIn(1); }); audio.controls = true; document.body.appendChild(audio); }); // Reiniciar las variables chunks = []; mediaRecorder = null; clearInterval(timerInterval); } function cancelRecording() { mediaRecorder.stop() chunks = []; mediaRecorder = null; clearInterval(timerInterval); $('#record-submit').show() $('#stop-record-submit').hide() $('#cancel-record-submit').hide() $('#divWaveAudio').hide() $('#custom-file-upload').show() $('#divInputChat').show() resetAnimation(); } function blobToBase64(blob) { var reader = new FileReader(); reader.readAsDataURL(blob); reader.onloadend = function () { var base64 = reader.result; var replaceBase64 = reader.result; base64 = base64.substr(base64.indexOf(',') + 1) sendAudio(base64, replaceBase64) } } function sendAudio(replaceBase64, base64) { var idsession_ = $("#idsession").val(); var operation_ = $("#idcliente").val(); var skill_ = $("#skill").val(); var formData1 = new FormData(); formData1.append("audio", replaceBase64); formData1.append("idsession", idsession_); formData1.append("operation", operation_); formData1.append("skill", skill_); $.ajax({ url: "querys/enviarAudio.php", data: formData1, type: "POST", dataType: "json", processData: false, contentType: false, success: function (response) { sendMsgType = response.sendMsgType; $("#loading-record").fadeOut(1, function () { $("#record-submit").fadeIn(1); }); if (response.etiqueta) { generate_message(response.etiqueta, "self"); if (response.sendMsgType == 1) { $msg = response.bucketUrl; $idcliente = $idcliente.replace(" ", ""); $skill = $skill.replace(" ", ""); chatbotmsg($text1, $text2, $text3, $text4, $text5, $text6, $idsession, $idcliente, $skill, $msg, "", ""); } } }, error: function (xhr) { $("#loading-record").fadeOut(1, function () { $("#record-submit").fadeIn(1); }); }, }); } function validateExtensionAudio(url) { var extension = url.split('.').pop().toLowerCase(); var extensions = ['mp3', 'wav', 'ogg']; // Lista de extensiones válidas if (extensions.includes(extension)) { return true; } else { return false; } } function validateBase64Audio(url) { // Obtener el tipo de contenido desde la URL base64 var contentType = url.split(';')[0].split(':')[1]; // Lista de tipos de contenido de audio válidos var validAudioTypes = ['audio/mp3', 'audio/wav', 'audio/ogg']; // Verificar si el tipo de contenido está en la lista de tipos de audio válidos if (validAudioTypes.includes(contentType)) { return true; } else { return false; } } function isImageAttachmentUrl(url) { if (!url || typeof url !== 'string') return false; if (url.indexOf('data:image/') === 0) return true; var ext = url.split('.').pop().toLowerCase().split('?')[0]; return ['jpg', 'jpeg', 'png', 'gif', 'webp'].indexOf(ext) !== -1; } function wvxScrollChatLogsToBottom(duration) { var logs = $(".chat-logs")[0]; if (!logs) return; var ms = typeof duration === 'number' ? duration : 300; $(".chat-logs").stop().animate({ scrollTop: logs.scrollHeight }, ms); } /** HTML guardado en BD con sendAttach antiguo: img.imageStyle con height:60%. Unifica a 50% en el markup renderizado. */ function wvxNormalizeImageStyleInline(msg) { if (!msg || typeof msg !== 'string' || msg.indexOf('imageStyle') === -1) return msg; return msg.replace(/width\s*:\s*60%\s*;\s*height\s*:\s*60%\s*;/gi, 'width:60%;height:50%;'); } function wvxBindChatImageLayoutFix($img) { if (!$img || !$img.length) return; var onDone = function () { wvxScrollChatLogsToBottom(300); }; $img.off("load.wvxAttach").on("load.wvxAttach", onDone); $img.off("error.wvxAttach").on("error.wvxAttach", onDone); if ($img[0].complete && $img[0].naturalWidth > 0) { setTimeout(onDone, 0); } } function updateTimer() { var currentTime = new Date(); var elapsedTime = Math.floor((currentTime - startTime) / 1000); // Tiempo transcurrido en segundos if (elapsedTime >= 60) { stopRecording(); // Pausar la grabación si se alcanza el minuto } // Actualizar el contador en tu interfaz (por ejemplo, en un elemento con el id "timer") document.getElementById('div-timer-record').textContent = formatTime(elapsedTime); } function formatTime(seconds) { var minutes = Math.floor(seconds / 60); var remainingSeconds = seconds % 60; // Agregar ceros iniciales si es necesario var formattedMinutes = minutes < 10 ? '0' + minutes : minutes; var formattedSeconds = remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds; return formattedMinutes + ':' + formattedSeconds; } obj = {} function init() { obj.canvas = document.getElementById('canvasWave'); let container = document.getElementById('divWaveAudio'); obj.ctx = obj.canvas.getContext('2d'); obj.width = container.clientWidth; obj.height = container.clientHeight * 0.9; obj.canvas.width = obj.width * window.devicePixelRatio; obj.canvas.height = obj.height * window.devicePixelRatio; obj.canvas.style.width = obj.width + 'px'; obj.canvas.style.height = obj.height + 'px'; obj.ctx.scale(window.devicePixelRatio, window.devicePixelRatio); } function randomInteger(max = 256) { return Math.floor(Math.random() * max); } let timeOffset = 100; let now = parseInt(performance.now()) / timeOffset; function loop() { obj.ctx.clearRect(0, 0, obj.canvas.width, obj.canvas.height); let max = 0; if (parseInt(performance.now() / timeOffset) > now) { now = parseInt(performance.now() / timeOffset); obj.analyser.getFloatTimeDomainData(obj.frequencyArray) for (var i = 0; i < obj.frequencyArray.length; i++) { if (obj.frequencyArray[i] > max) { max = obj.frequencyArray[i]; } } var freq = Math.floor(max * 650); obj.bars.push({ x: obj.width, y: (obj.height / 2) - (freq / 2), height: (freq == 0) ? 3 : freq + 3, width: 3 }); } draw(); animationId = requestAnimationFrame(loop); } obj.bars = []; function draw() { for (i = 0; i < obj.bars.length; i++) { const bar = obj.bars[i]; obj.ctx.fillRect(bar.x, bar.y, bar.width, bar.height); bar.x = bar.x - 1; if (bar.x < 1) { obj.bars.splice(i, 1) } } } function resetAnimation() { cancelAnimationFrame(animationId); obj.bars = []; now = parseInt(performance.now()) / timeOffset; } // SOLUCIÓN: Función para restaurar persistencia de botones function restoreButtonPersistence() { // Buscar todos los botones en la página var allButtons = document.querySelectorAll('.interactive_button_wolkvox'); var restoredCount = 0; allButtons.forEach(function (button, index) { var buttonId = button.id; var localStorageKey = 'button_selected_' + buttonId; var localStorageValue = localStorage.getItem(localStorageKey); if (localStorageValue === 'true') { button.classList.add('selected'); restoredCount++; } }); } // SOLUCIÓN: Ejecutar restauración de persistencia cuando se carga la página document.addEventListener('DOMContentLoaded', function () { // Restaurar inmediatamente restoreButtonPersistence(); // Restaurar después de un delay por si acaso setTimeout(function () { restoreButtonPersistence(); }, 500); // Restaurar después de otro delay setTimeout(function () { restoreButtonPersistence(); }, 1000); }); // SOLUCIÓN: También ejecutar cuando se completa la carga de la ventana window.addEventListener('load', function () { restoreButtonPersistence(); // SOLUCIÓN: Mantener persistencia activa cada 2 segundos setInterval(function () { restoreButtonPersistence(); }, 2000); }); /*console.clear();*/