Files
sistema_funcionando_lastwar/chat_telegram.php

622 lines
27 KiB
PHP
Executable File

<?php
require_once __DIR__ . '/includes/session_check.php';
require_once __DIR__ . '/includes/db.php';
// Solo para administradores
if ($_SESSION['role'] !== 'admin') {
header('HTTP/1.0 403 Forbidden');
die('Acceso denegado.');
}
// Obtener todos los usuarios de Telegram que han interactuado o son miembros de grupos
$stmt = $pdo->query(
"SELECT DISTINCT r.platform_id, r.name
FROM recipients r
WHERE r.platform = 'telegram' AND r.type = 'user'
ORDER BY r.name ASC"
);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
require_once __DIR__ . '/templates/header.php';
?>
<style>
.chat-container {
display: flex;
height: calc(100vh - 200px); /* Ajustar altura */
}
/* Estilos para el selector de emojis */
.emoji-picker {
position: fixed;
width: 250px;
max-height: 300px;
overflow-y: auto;
background: white;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 9999;
display: none;
flex-wrap: wrap;
gap: 5px;
}
.emoji-option {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
font-size: 1.5em;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
user-select: none;
}
.emoji-option:hover {
background-color: #f8f9fa;
transform: scale(1.2);
}
.emoji-option:active {
transform: scale(0.95);
}
.input-group-prepend .btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
#message-text {
border-radius: 0;
}
.message-text {
white-space: pre-wrap;
word-wrap: break-word;
}
.user-list {
border-right: 1px solid #dee2e6;
overflow-y: auto;
}
.chat-history {
flex-grow: 1;
overflow-y: auto;
padding: 1rem;
display: flex;
flex-direction: column;
}
.message-form {
padding: 1rem;
border-top: 1px solid #dee2e6;
}
.message {
max-width: 70%;
padding: 0.5rem 1rem;
border-radius: 1rem;
margin-bottom: 0.5rem;
}
.message.in {
background-color: #6495ED; /* Azul claro (CornflowerBlue) */
color: white;
align-self: flex-start;
}
.message.out {
background-color: #6495ED; /* Azul claro (CornflowerBlue) */
color: white;
align-self: flex-end;
}
.user-list .list-group-item.active {
background-color: #0d6efd;
border-color: #0d6efd;
}
</style>
<div class="container-fluid">
<h1 class="mt-4">Chat de Soporte de Telegram</h1>
<div class="card shadow-sm">
<div class="card-body p-0">
<div class="chat-container">
<!-- Columna de Usuarios -->
<div class="col-md-4 col-lg-3 user-list">
<div class="list-group list-group-flush">
<?php foreach ($users as $user): ?>
<a href="#" class="list-group-item list-group-item-action" data-chat-id="<?= $user['platform_id'] ?>">
<?= htmlspecialchars($user['name']) ?>
</a>
<?php endforeach; ?>
</div>
</div>
<!-- Columna de Chat -->
<div class="col-md-8 col-lg-9 d-flex flex-column">
<div id="chat-history" class="chat-history">
<div class="text-center text-muted my-auto">
<i class="bi bi-arrow-left-circle-fill fs-1"></i>
<p>Selecciona un usuario para ver la conversación.</p>
</div>
</div>
<div id="message-form-container" class="message-form" style="display: none;">
<form id="message-form">
<input type="hidden" id="chat-id-input" name="chat_id">
<div class="input-group" style="position: relative;">
<div class="input-group-prepend">
<button type="button" id="emoji-trigger" class="btn btn-outline-secondary" onclick="toggleEmojiPicker()">
<i class="bi bi-emoji-smile"></i>
</button>
</div>
<input type="text" id="message-text" name="message" class="form-control" placeholder="Escribe tu respuesta..." required>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Enviar</button>
</div>
<!-- Selector de emojis simplificado -->
<div id="emoji-picker" style="
display: none;
position: absolute;
bottom: 100%;
left: 0;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
width: 250px;
max-height: 200px;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 1000;">
<div style="display: flex; flex-wrap: wrap; gap: 5px;">
<span class="emoji-option" onclick="insertEmoji('😊')" style="cursor: pointer; font-size: 24px;">😊</span>
<span class="emoji-option" onclick="insertEmoji('😂')" style="cursor: pointer; font-size: 24px;">😂</span>
<span class="emoji-option" onclick="insertEmoji('😍')" style="cursor: pointer; font-size: 24px;">😍</span>
<span class="emoji-option" onclick="insertEmoji('😎')" style="cursor: pointer; font-size: 24px;">😎</span>
<span class="emoji-option" onclick="insertEmoji('😢')" style="cursor: pointer; font-size: 24px;">😢</span>
<span class="emoji-option" onclick="insertEmoji('😡')" style="cursor: pointer; font-size: 24px;">😡</span>
<span class="emoji-option" onclick="insertEmoji('😴')" style="cursor: pointer; font-size: 24px;">😴</span>
<span class="emoji-option" onclick="insertEmoji('😷')" style="cursor: pointer; font-size: 24px;">😷</span>
<span class="emoji-option" onclick="insertEmoji('🤔')" style="cursor: pointer; font-size: 24px;">🤔</span>
<span class="emoji-option" onclick="insertEmoji('🤗')" style="cursor: pointer; font-size: 24px;">🤗</span>
<span class="emoji-option" onclick="insertEmoji('👍')" style="cursor: pointer; font-size: 24px;">👍</span>
<span class="emoji-option" onclick="insertEmoji('👎')" style="cursor: pointer; font-size: 24px;">👎</span>
<span class="emoji-option" onclick="insertEmoji('👏')" style="cursor: pointer; font-size: 24px;">👏</span>
<span class="emoji-option" onclick="insertEmoji('🙌')" style="cursor: pointer; font-size: 24px;">🙌</span>
<span class="emoji-option" onclick="insertEmoji('🤝')" style="cursor: pointer; font-size: 24px;">🤝</span>
<span class="emoji-option" onclick="insertEmoji('📱')" style="cursor: pointer; font-size: 24px;">📱</span>
<span class="emoji-option" onclick="insertEmoji('💻')" style="cursor: pointer; font-size: 24px;">💻</span>
<span class="emoji-option" onclick="insertEmoji('📷')" style="cursor: pointer; font-size: 24px;">📷</span>
<span class="emoji-option" onclick="insertEmoji('🎮')" style="cursor: pointer; font-size: 24px;">🎮</span>
<span class="emoji-option" onclick="insertEmoji('📚')" style="cursor: pointer; font-size: 24px;">📚</span>
</div>
</div>
</div>
<script>
// Función para mostrar/ocultar el selector de emojis
function toggleEmojiPicker() {
const picker = document.getElementById('emoji-picker');
if (picker.style.display === 'none' || !picker.style.display) {
picker.style.display = 'block';
} else {
picker.style.display = 'none';
}
return false;
}
// Función para insertar un emoji en el campo de texto
function insertEmoji(emoji) {
const input = document.getElementById('message-text');
const start = input.selectionStart;
const end = input.selectionEnd;
const text = input.value;
input.value = text.substring(0, start) + emoji + text.substring(end);
input.focus();
input.setSelectionRange(start + emoji.length, start + emoji.length);
document.getElementById('emoji-picker').style.display = 'none';
}
// Cerrar el selector al hacer clic fuera de él
document.addEventListener('click', function(e) {
const picker = document.getElementById('emoji-picker');
const trigger = document.getElementById('emoji-trigger');
if (picker && trigger &&
e.target !== picker && !picker.contains(e.target) &&
e.target !== trigger && !trigger.contains(e.target)) {
picker.style.display = 'none';
}
});
</script>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<?php require_once __DIR__ . '/templates/footer.php'; ?>
<script>
// Función para insertar emojis en el campo de texto
function insertEmoji(emoji) {
const input = document.getElementById('message-text');
const start = input.selectionStart;
const end = input.selectionEnd;
const text = input.value;
// Insertar el emoji en la posición del cursor
input.value = text.substring(0, start) + emoji + text.substring(end);
// Mover el cursor después del emoji insertado
const newPos = start + emoji.length;
input.selectionStart = input.selectionEnd = newPos;
// Enfocar el campo de texto
input.focus();
}
document.addEventListener('DOMContentLoaded', function() {
const userLinks = document.querySelectorAll('.user-list .list-group-item');
const chatHistory = document.getElementById('chat-history');
const formContainer = document.getElementById('message-form-container');
const messageForm = document.getElementById('message-form');
const chatIdInput = document.getElementById('chat-id-input');
const messageTextInput = document.getElementById('message-text');
let activeChatId = null;
let lastMessageId = 0;
let refreshInterval = null;
userLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
// Marcar usuario activo
userLinks.forEach(l => l.classList.remove('active'));
this.classList.add('active');
// Detener el intervalo anterior si existe
if (refreshInterval) {
clearInterval(refreshInterval);
}
// Establecer el nuevo chat activo
activeChatId = this.getAttribute('data-chat-id');
chatIdInput.value = activeChatId;
lastMessageId = 0; // Reiniciar el ID del último mensaje
// Cargar el historial y comenzar a actualizar
loadChatHistory(activeChatId);
// Iniciar la actualización automática con un intervalo dinámico
let refreshDelay = 500; // Comenzar con 0.5 segundos
function startRefreshTimer() {
if (refreshInterval) clearInterval(refreshInterval);
refreshInterval = setInterval(() => {
if (activeChatId && !document.hidden) {
checkForNewMessages(activeChatId);
}
}, refreshDelay);
}
// Iniciar el temporizador
startRefreshTimer();
});
});
function loadChatHistory(chatId, onlyNew = false) {
// Usar un timestamp para evitar caché del navegador
const timestamp = new Date().getTime();
const url = `get_chat_history.php?chat_id=${chatId}${onlyNew ? '&last_id=' + lastMessageId : ''}&_=${timestamp}`;
if (!onlyNew) {
chatHistory.innerHTML = '<div class="text-center text-muted my-auto"><div class="spinner-border" role="status"><span class="visually-hidden">Cargando...</span></div></div>';
formContainer.style.display = 'block';
}
fetch(url)
.then(response => response.json())
.then(data => {
console.log('Datos recibidos:', data); // Depuración
if (onlyNew && (!data.history || data.history.length === 0)) {
return; // No hay mensajes nuevos
}
if (!onlyNew) {
chatHistory.innerHTML = '';
}
let hasNewMessages = false;
if (data.success && data.history && data.history.length > 0) {
// Ordenar los mensajes por ID para asegurar el orden correcto
data.history.sort((a, b) => a.id - b.id);
// Ordenar los mensajes por ID para asegurar el orden correcto
data.history.sort((a, b) => a.id - b);
data.history.forEach(msg => {
// Solo agregar mensajes más recientes que el último mostrado
if (msg.id > lastMessageId || !onlyNew) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message', msg.direction);
// Usar innerHTML para permitir que el enlace de traducción se añada correctamente
messageDiv.innerHTML = `<div class="message-text">${msg.message_text.replace(/\n/g, '<br>')}</div>`;
// Add translate link if needed
// For testing, always show for incoming messages
if (msg.direction === 'in') { // msg.language_code !== 'es'
const translateLink = document.createElement('a');
translateLink.href = '#';
translateLink.textContent = ' (Traducir)';
translateLink.classList.add('translate-link');
translateLink.setAttribute('data-message-id', msg.id);
messageDiv.appendChild(translateLink);
}
// Agregar el mensaje al historial
chatHistory.appendChild(messageDiv);
// Actualizar el ID del último mensaje
if (msg.id > lastMessageId) {
lastMessageId = msg.id;
}
hasNewMessages = true;
}
});
// Desplazarse al final solo si hay mensajes nuevos
if (hasNewMessages) {
chatHistory.scrollTop = chatHistory.scrollHeight;
}
} else if (!onlyNew) {
chatHistory.innerHTML = '<div class="text-center text-muted my-auto">No hay mensajes en esta conversación.</div>';
}
// Si no es una actualización, desplazarse al final
if (!onlyNew) {
chatHistory.scrollTop = chatHistory.scrollHeight;
}
// Actualizar lastMessageId si viene en la respuesta
if (data.last_id && data.last_id > lastMessageId) {
lastMessageId = data.last_id;
}
})
.catch(error => {
if (!onlyNew) {
console.error('Error al cargar el historial:', error);
chatHistory.innerHTML = '<div class="text-center text-danger my-auto">Error al cargar el historial. Intenta recargar la página.</div>';
}
});
}
// Función para verificar mensajes nuevos
function checkForNewMessages(chatId) {
if (!document.hidden && chatId) {
// Usar fetch con headers para evitar caché
fetch(`get_chat_history.php?chat_id=${chatId}&last_id=${lastMessageId}&_=${new Date().getTime()}`, {
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
}
})
.then(response => response.json())
.then(data => {
console.log('Datos recibidos en checkForNewMessages:', data); // Depuración
if (data.success && data.history && data.history.length > 0) {
let hasNewMessages = false;
// Ordenar los mensajes por ID para asegurar el orden correcto
data.history.sort((a, b) => a.id - b);
data.history.forEach(msg => {
if (msg.id > lastMessageId) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message', msg.direction);
messageDiv.textContent = msg.message_text;
chatHistory.appendChild(messageDiv);
if (msg.id > lastMessageId) {
lastMessageId = msg.id;
}
hasNewMessages = true;
}
});
if (hasNewMessages) {
chatHistory.scrollTop = chatHistory.scrollHeight;
// Si hay mensajes nuevos, reiniciar el temporizador con el intervalo más corto
refreshDelay = 2000;
startRefreshTimer();
}
}
if (data.last_id && data.last_id > lastMessageId) {
lastMessageId = data.last_id;
}
})
.catch(error => {
console.error('Error al verificar mensajes nuevos:', error);
});
}
}
// Actualizar el chat cuando la pestaña vuelve a estar activa
document.addEventListener('visibilitychange', function() {
if (!document.hidden && activeChatId) {
loadChatHistory(activeChatId);
}
// Inicializar el selector de emojis
console.log('Inicializando selector de emojis...');
let emojiPickerVisible = false;
const emojiTrigger = document.getElementById('emoji-trigger');
const emojiPicker = document.getElementById('emoji-picker');
const messageTextInput = document.getElementById('message-text');
console.log('Elementos del selector de emojis:', {
emojiTrigger: emojiTrigger ? 'Encontrado' : 'No encontrado',
emojiPicker: emojiPicker ? 'Encontrado' : 'No encontrado',
messageTextInput: messageTextInput ? 'Encontrado' : 'No encontrado'
});
// Asegurarse de que el selector esté oculto inicialmente
if (emojiPicker) {
emojiPicker.style.display = 'none';
emojiPicker.style.backgroundColor = '#fff';
emojiPicker.style.border = '2px solid red';
emojiPicker.innerHTML = '<div style="padding: 10px;">Selector de emojis</div>' + emojiPicker.innerHTML;
}
// Mostrar/ocultar selector de emojis
if (emojiTrigger) {
emojiTrigger.addEventListener('click', function(e) {
console.log('Botón de emoji clickeado');
e.preventDefault();
e.stopPropagation();
if (!emojiPicker) {
console.error('El selector de emojis no se encontró en el DOM');
return false;
}
if (emojiPickerVisible) {
console.log('Ocultando selector de emojis');
emojiPicker.style.display = 'none';
} else {
console.log('Mostrando selector de emojis');
emojiPicker.style.display = 'block';
// Posicionar el selector debajo del botón
const rect = emojiTrigger.getBoundingClientRect();
emojiPicker.style.position = 'fixed';
emojiPicker.style.top = (rect.bottom + window.scrollY) + 'px';
emojiPicker.style.left = (rect.left + window.scrollX) + 'px';
emojiPicker.style.zIndex = '9999';
console.log('Posición del selector:', emojiPicker.style.top, emojiPicker.style.left);
}
emojiPickerVisible = !emojiPickerVisible;
return false;
});
} else {
console.error('No se pudo encontrar el botón de emoji');
}
// Cerrar el selector al hacer clic fuera
document.addEventListener('click', function(e) {
if (!emojiPicker || !emojiTrigger) return;
const isClickInside = emojiPicker.contains(e.target) || emojiTrigger.contains(e.target);
if (emojiPickerVisible && !isClickInside) {
console.log('Clic fuera del selector, ocultando...');
emojiPicker.style.display = 'none';
emojiPickerVisible = false;
}
});
// Manejar clics en emojis
if (emojiPicker) {
emojiPicker.addEventListener('click', function(e) {
console.log('Clic en el selector de emojis');
const emojiOption = e.target.closest('.emoji-option');
if (emojiOption) {
const emojiChar = emojiOption.getAttribute('data-emoji');
console.log('Emoji seleccionado:', emojiChar);
if (emojiChar) {
insertEmoji(emojiChar);
emojiPicker.style.display = 'none';
emojiPickerVisible = false;
}
}
});
}
});
// Evento para enviar mensaje
messageForm.addEventListener('submit', function(e) {
e.preventDefault();
const messageText = messageTextInput.value.trim();
if (!messageText) return;
const formData = new FormData(this);
// Optimistic UI update
const messageDiv = document.createElement('div');
messageDiv.className = `message out`;
messageDiv.innerHTML = `
<div class="message-text">${messageText.replace(/\n/g, '<br>')}</div>
<div class="message-time">${new Date().toLocaleTimeString()}</div>
`;
chatHistory.appendChild(messageDiv);
chatHistory.scrollTop = chatHistory.scrollHeight;
messageTextInput.value = '';
fetch('admin_send_message.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
alert('Error al enviar el mensaje: ' + data.error);
// Opcional: eliminar el mensaje de la UI si falla
chatHistory.removeChild(messageDiv);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error de red al enviar el mensaje.');
chatHistory.removeChild(messageDiv);
});
});
// Evento para traducir mensaje
chatHistory.addEventListener('click', function(e) {
if (e.target.classList.contains('translate-link')) {
e.preventDefault();
const messageId = e.target.getAttribute('data-message-id');
const messageDiv = e.target.parentElement;
fetch(`translate_message.php?message_id=${messageId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const translatedText = document.createElement('div');
translatedText.classList.add('translated-text');
translatedText.textContent = data.translated_text;
messageDiv.appendChild(translatedText);
e.target.remove(); // Remove the translate link
} else {
alert('Error al traducir el mensaje: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error de red al traducir el mensaje.');
});
}
});
});
</script>