Tul xxx Tul
User / IP
:
216.73.216.146
Host / Server
:
45.84.207.204 / aircan.me
System
:
Linux lt-bnk-web1726.main-hosting.eu 5.14.0-611.36.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Mar 3 11:23:52 EST 2026 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
u931257429
/
domains
/
aircan.me
/
public_html
/
app
/
views
/
chat
/
Viewing: admin_index.php
<div class="row g-4 h-100" style="min-height: calc(100vh - 180px);"> <!-- Lista de Contactos --> <div class="col-md-4 h-100"> <div class="card-custom h-100 d-flex flex-column" style="border-radius: 20px; overflow: hidden;"> <div class="card-header-custom p-4 bg-white border-bottom"> <h5 class="fw-bold mb-0"><i class="bi bi-people me-2 text-primary"></i>Conversaciones</h5> </div> <div class="card-body-custom flex-grow-1 overflow-auto p-0" id="contact-list"> <!-- Cargado por JS --> </div> </div> </div> <!-- Área de Chat --> <div class="col-md-8 h-100"> <div class="card-custom h-100 d-flex flex-column" id="chat-container" style="border-radius: 20px; overflow: hidden; background: #f8f9fa;"> <!-- Estado Vacío --> <div id="chat-empty" class="flex-grow-1 d-flex flex-column align-items-center justify-content-center opacity-50"> <div class="bg-white rounded-circle shadow-sm p-4 mb-3"> <i class="bi bi-chat-left-dots fs-1 text-primary"></i> </div> <h5 class="fw-bold">Bandeja de Entrada</h5> <p>Selecciona un cliente para iniciar el chat.</p> </div> <!-- Chat Activo --> <div id="chat-active" class="d-none h-100 flex-grow-1 d-flex flex-column"> <div class="card-header-custom p-3 bg-white border-bottom d-flex justify-content-between align-items-center shadow-sm"> <div class="d-flex align-items-center gap-3"> <div id="active-avatar" class="d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;"></div> <div> <h6 id="active-name" class="fw-bold mb-0"></h6> <div class="small text-success"><i class="bi bi-circle-fill me-1" style="font-size: 0.5rem;"></i> En línea</div> </div> </div> </div> <div id="chat-messages-admin" class="flex-grow-1 p-4 overflow-auto d-flex flex-column gap-3 position-relative" style="background: url('https://user-images.githubusercontent.com/15075759/28719144-86dc0f70-73b1-11e7-911d-60d70fcded21.png'); max-height: calc(100vh - 400px); min-height: 400px;"> <!-- Mensajes cargados por JS --> <!-- Botón flotante para ir al final --> <button id="scroll-to-bottom-btn" class="btn btn-primary rounded-circle shadow-lg position-absolute d-none" style="bottom: 20px; right: 20px; width: 45px; height: 45px; z-index: 10; transition: all 0.3s ease;" onclick="scrollToBottom()"> <i class="bi bi-arrow-down"></i> </button> </div> <div class="card-footer-custom p-3 bg-white border-top"> <form id="chat-form-admin" class="d-flex gap-3 align-items-center"> <input type="text" id="chat-input-admin" class="form-control" placeholder="Escribe tu mensaje aquí..." style="border-radius: 12px; padding: 12px 20px; border: 1.5px solid #eee;" autocomplete="off"> <button type="submit" id="chat-send-btn-admin" class="btn btn-primary-custom rounded-pill px-4 py-2 d-flex align-items-center gap-2" style="font-weight: 600;"> Enviar <i class="bi bi-send-fill"></i> </button> </form> </div> </div> </div> </div> </div> <style> .cursor-pointer { cursor: pointer; } .hover-bg-light:hover { background: #fcfdfe; } .contact-item { border-left: 4px solid transparent; transition: all 0.2s ease; } .contact-item.active { background: #f0f3ff; border-left: 4px solid #4361ee; } /* Estilos mejorados del scroll del chat */ #chat-messages-admin { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: rgba(67, 97, 238, 0.3) transparent; } #chat-messages-admin::-webkit-scrollbar { width: 8px; } #chat-messages-admin::-webkit-scrollbar-track { background: transparent; border-radius: 10px; } #chat-messages-admin::-webkit-scrollbar-thumb { background: rgba(67, 97, 238, 0.3); border-radius: 10px; transition: background 0.2s ease; } #chat-messages-admin::-webkit-scrollbar-thumb:hover { background: rgba(67, 97, 238, 0.5); } /* Estilos de los mensajes */ .msg-bubble { max-width: 75%; padding: 12px 16px; border-radius: 20px; font-size: 0.9rem; line-height: 1.5; position: relative; box-shadow: 0 2px 5px rgba(0,0,0,0.05); animation: fadeIn 0.3s ease; word-wrap: break-word; overflow-wrap: break-word; } .msg-mine { align-self: flex-end; background: linear-gradient(135deg, #4361ee 0%, #5a7cee 100%); color: white; border-bottom-right-radius: 4px; } .msg-theirs { align-self: flex-start; background: white; color: #111827; border-bottom-left-radius: 4px; border: 1px solid rgba(0,0,0,0.05); } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } /* Scroll suave al cargar */ #chat-messages-admin.loading { opacity: 0.7; pointer-events: none; } /* Botón flotante de scroll */ #scroll-to-bottom-btn { animation: bounceIn 0.3s ease; } #scroll-to-bottom-btn:hover { transform: scale(1.1); } @keyframes bounceIn { 0% { opacity: 0; transform: scale(0.3); } 50% { opacity: 1; transform: scale(1.05); } 100% { transform: scale(1); } } /* Mejorar el área de contactos con scroll */ #contact-list { max-height: calc(100vh - 280px); overflow-y: auto; scrollbar-width: thin; scrollbar-color: rgba(67, 97, 238, 0.2) transparent; } #contact-list::-webkit-scrollbar { width: 6px; } #contact-list::-webkit-scrollbar-track { background: transparent; } #contact-list::-webkit-scrollbar-thumb { background: rgba(67, 97, 238, 0.2); border-radius: 10px; } #contact-list::-webkit-scrollbar-thumb:hover { background: rgba(67, 97, 238, 0.4); } </style> <script> (function() { let selectedContactId = null; let pollMessages = null; let lastMsgCount = 0; let currentConversationId = null; // Nueva variable para rastrear la conversación actual const contactList = document.getElementById('contact-list'); const chatEmpty = document.getElementById('chat-empty'); const chatActive = document.getElementById('chat-active'); const activeName = document.getElementById('active-name'); const activeAvatar = document.getElementById('active-avatar'); const messagesAdmin = document.getElementById('chat-messages-admin'); const chatFormAdmin = document.getElementById('chat-form-admin'); const chatInputAdmin = document.getElementById('chat-input-admin'); const sendBtnAdmin = document.getElementById('chat-send-btn-admin'); const scrollToBottomBtn = document.getElementById('scroll-to-bottom-btn'); // Función global para scroll al final window.scrollToBottom = function() { messagesAdmin.scrollTo({ top: messagesAdmin.scrollHeight, behavior: 'smooth' }); }; // Detectar scroll para mostrar/ocultar botón messagesAdmin.addEventListener('scroll', function() { const isAtBottom = messagesAdmin.scrollHeight - messagesAdmin.scrollTop <= messagesAdmin.clientHeight + 100; if (isAtBottom) { scrollToBottomBtn.classList.add('d-none'); } else { scrollToBottomBtn.classList.remove('d-none'); } }); // Función para cargar contactos async function loadContacts() { try { const response = await fetch('<?= BASE_URL ?>/chat/getContactos'); const data = await response.json(); if (data.success) { renderContacts(data.contactos); } } catch (error) { console.error('Contacts Load Error:', error); } } function renderContacts(contactos) { if (contactos.length === 0) { contactList.innerHTML = '<div class="text-center py-5 opacity-25"><i class="bi bi-person-x fs-1"></i><p class="mt-2">No hay clientes registrados.</p></div>'; return; } contactList.innerHTML = contactos.map(c => { const inicial = c.nombre.charAt(0).toUpperCase(); const avatarHtml = c.foto ? `<img src="<?= BASE_URL ?>/public/assets/uploads/clientes/${c.foto}" class="rounded-circle" style="width: 45px; height: 45px; object-fit: cover; border: 2px solid rgba(67, 97, 238, 0.1);" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> <div class="bg-primary text-white rounded-circle d-none align-items-center justify-content-center fw-bold" style="width: 45px; height: 45px;">${inicial}</div>` : `<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center fw-bold" style="width: 45px; height: 45px;">${inicial}</div>`; return ` <div class="contact-item p-3 border-bottom cursor-pointer hover-bg-light position-relative ${c.id == selectedContactId ? 'active' : ''}" data-contact-id="${c.id}" data-contact-photo="${c.foto || ''}" onclick="window.selectContact(${c.id}, '${c.nombre.replace(/'/g, "\\'")}', '${c.foto || ''}')"> <div class="d-flex align-items-center gap-3"> ${avatarHtml} <div class="flex-grow-1 overflow-hidden"> <div class="d-flex justify-content-between align-items-center"> <h6 class="fw-bold mb-0 text-truncate" style="font-size: 0.95rem;">${c.nombre}</h6> ${c.no_leidos > 0 ? `<span class="badge rounded-pill bg-danger">${c.no_leidos}</span>` : ''} </div> <div class="text-muted small text-truncate"> ${c.ultimo_mensaje || 'Inicia una conversación'} </div> </div> </div> </div> `; }).join(''); } // Función global para seleccionar contacto window.selectContact = function(id, name, photo) { // Limpiar intervalo anterior if (pollMessages) { clearInterval(pollMessages); pollMessages = null; } // Actualizar IDs selectedContactId = id; currentConversationId = id; lastMsgCount = 0; // Limpiar mensajes anteriores inmediatamente messagesAdmin.innerHTML = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Cargando...</span></div></div>'; // Mostrar chat activo chatEmpty.classList.add('d-none'); chatActive.classList.remove('d-none'); activeName.textContent = name; // Actualizar avatar con foto o inicial const inicial = name.charAt(0).toUpperCase(); if (photo) { activeAvatar.innerHTML = ` <img src="<?= BASE_URL ?>/public/assets/uploads/clientes/${photo}" class="rounded-circle" style="width: 40px; height: 40px; object-fit: cover;" onerror="this.style.display='none'; this.parentElement.innerHTML='<div class=\\'bg-primary text-white rounded-circle d-flex align-items-center justify-content-center fw-bold\\' style=\\'width: 40px; height: 40px;\\'>${inicial}</div>';"> `; } else { activeAvatar.innerHTML = ` <div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center fw-bold" style="width: 40px; height: 40px;">${inicial}</div> `; } // Actualizar visual de lista document.querySelectorAll('.contact-item').forEach(el => el.classList.remove('active')); const activeItem = document.querySelector(`.contact-item[data-contact-id="${id}"]`); if (activeItem) activeItem.classList.add('active'); // Cargar mensajes inmediatamente loadMessages(id); // Iniciar polling solo para este contacto pollMessages = setInterval(() => { // Solo cargar si seguimos en la misma conversación if (currentConversationId === id) { loadMessages(id); } }, 3000); chatInputAdmin.focus(); }; async function loadMessages(contactId) { // Usar el contactId pasado como parámetro en lugar de la variable global const idToLoad = contactId || selectedContactId; if (!idToLoad) return; // Verificar que seguimos en la misma conversación if (contactId && contactId !== currentConversationId) { return; // No cargar si cambiamos de conversación } try { const response = await fetch('<?= BASE_URL ?>/chat/getMensajes/' + idToLoad); const data = await response.json(); // Verificar nuevamente que seguimos en la misma conversación if (data.success && data.destinatario_id == currentConversationId) { if (data.mensajes.length !== lastMsgCount) { renderAdminMessages(data.mensajes, data.mi_id); lastMsgCount = data.mensajes.length; } } } catch (error) { console.error('Messages Load Error:', error); } } function renderAdminMessages(msgs, miId) { // Guardar posición actual del scroll const wasAtBottom = messagesAdmin.scrollHeight - messagesAdmin.scrollTop <= messagesAdmin.clientHeight + 100; messagesAdmin.innerHTML = msgs.map(m => ` <div class="msg-bubble ${m.remitente_id == miId ? 'msg-mine' : 'msg-theirs'}"> ${escapeHtml(m.mensaje)} <div class="small opacity-50 mt-1" style="font-size: 0.65rem; text-align: right;"> ${new Date(m.fecha_envio.replace(/-/g, '/')).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} </div> </div> `).join(''); // Solo hacer scroll al final si estábamos al final o es la primera carga if (wasAtBottom || lastMsgCount === 0) { setTimeout(() => { messagesAdmin.scrollTop = messagesAdmin.scrollHeight; }, 100); } } // Función para escapar HTML y prevenir XSS function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } chatFormAdmin.addEventListener('submit', async function(e) { e.preventDefault(); const msg = chatInputAdmin.value.trim(); if (!msg || !selectedContactId) return; chatInputAdmin.disabled = true; sendBtnAdmin.disabled = true; const formData = new FormData(); formData.append('receptor_id', selectedContactId); formData.append('mensaje', msg); try { const response = await fetch('<?= BASE_URL ?>/chat/enviar', { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { chatInputAdmin.value = ''; // Cargar mensajes solo de la conversación actual await loadMessages(selectedContactId); await loadContacts(); } } catch (error) { console.error('Admin Send Error:', error); } finally { chatInputAdmin.disabled = false; sendBtnAdmin.disabled = false; chatInputAdmin.focus(); } }); // Polling de contactos loadContacts(); setInterval(loadContacts, 10000); // Limpiar intervalo cuando se cierra la página window.addEventListener('beforeunload', function() { if (pollMessages) { clearInterval(pollMessages); } }); })(); </script>
Coded With 💗 by
0x6ick