Files
sistema_para_juego/discord/views/messages/create.php

737 lines
29 KiB
PHP
Executable File

<?php
session_start();
// Habilitar logging para depuración
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/../../../shared/utils/helpers.php';
require_once __DIR__ . '/../../../shared/auth/jwt.php';
require_once __DIR__ . '/../../../shared/database/connection.php';
$userData = JWTAuth::requireAuth();
// Verificar permiso para enviar mensajes
if (!hasPermission('send_messages', 'discord')) {
die('No tienes permiso para crear y enviar mensajes de Discord.');
}
$db = getDB();
// Obtener plantillas para el selector
$stmt = $db->query("SELECT id, nombre, contenido FROM plantillas_discord ORDER BY nombre ASC");
$plantillas = $stmt->fetchAll();
// Obtener destinatarios guardados (si existen)
$stmt = $db->query("SELECT id, nombre, discord_id as identificador, tipo FROM destinatarios_discord ORDER BY nombre ASC");
$destinatarios = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="<?php echo $userData->idioma ?? 'es'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crear Mensaje Discord - Sistema de Bots</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/4.1.5/css/flag-icons.min.css" />
<style>
/* Estilos para los botones de traducción */
.translation-buttons {
margin-bottom: 10px;
padding: 5px;
border: 1px solid #e0e0e0;
border-radius: 4px;
background-color: #f8f9fa;
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.translation-buttons .btn-translate {
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 2px 8px;
font-size: 12px;
display: flex;
align-items: center;
gap: 5px;
background: white;
transition: all 0.2s;
}
.translation-buttons .btn-translate:hover {
background-color: #e9ecef;
transform: translateY(-1px);
}
.translation-buttons .btn-translate:active {
transform: translateY(0);
}
.translation-buttons .flag-icon {
width: 16px;
height: 12px;
border-radius: 2px;
box-shadow: 0 0 1px rgba(0,0,0,0.3);
}
.translation-buttons .loading {
opacity: 0.7;
pointer-events: none;
}
:root {
--discord-color: #5865F2;
--discord-dark: #4752C4;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, var(--discord-color) 0%, var(--discord-dark) 100%);
min-height: 100vh;
padding: 20px;
}
.header {
background: white;
border-radius: 15px;
padding: 20px 30px;
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.header h1 {
color: var(--discord-color);
font-size: 24px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.form-container {
background: white;
border-radius: 15px;
padding: 40px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 600;
font-size: 14px;
}
.form-control {
width: 100%;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: var(--discord-color);
}
.form-help {
font-size: 13px;
color: #666;
margin-top: 5px;
}
.btn {
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
border: none;
cursor: pointer;
font-size: 15px;
font-weight: 600;
}
.btn-primary {
background: var(--discord-color);
color: white;
}
.btn-primary:hover {
background: var(--discord-dark);
transform: translateY(-2px);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-success {
background: #28a745;
color: white;
}
.form-actions {
display: flex;
gap: 15px;
margin-top: 30px;
border-top: 1px solid #eee;
padding-top: 20px;
}
/* Select2 Customization */
.select2-container--default .select2-selection--single {
height: 45px;
border: 2px solid #e0e0e0;
border-radius: 8px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 45px;
padding-left: 15px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 43px;
}
/* Modal Galería */
.modal {
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
}
.modal-content {
background: white;
margin: 50px auto;
padding: 30px;
border-radius: 15px;
width: 90%;
max-width: 900px;
max-height: 80vh;
overflow-y: auto;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
margin-top: 20px;
}
.gallery-item {
cursor: pointer;
border: 3px solid transparent;
border-radius: 10px;
overflow: hidden;
transition: all 0.3s;
}
.gallery-item:hover {
border-color: var(--discord-color);
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 150px;
object-fit: cover;
display: block;
}
.close {
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
</style>
</head>
<body>
<div class="header">
<h1><i class="fas fa-paper-plane"></i> Crear Mensaje Discord</h1>
<a href="/discord/dashboard_discord.php" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Volver
</a>
</div>
<div class="container">
<div class="form-container">
<form id="messageForm">
<div class="form-group">
<label for="destinatario_id">Destinatarios (Canal ID o Usuario ID) *</label>
<div style="display: flex; gap: 10px;">
<div style="flex-grow: 1;">
<select id="destinatario_select" class="form-control" style="width: 100%;" multiple="multiple">
<?php foreach ($destinatarios as $dest): ?>
<option value="<?php echo htmlspecialchars($dest['identificador']); ?>">
<?php echo htmlspecialchars($dest['nombre']); ?> (<?php echo $dest['tipo']; ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div style="flex-grow: 1;">
<input type="text" id="destinatario_manual" name="destinatario_manual" class="form-control" placeholder="O pega IDs manualmente aquí, separados por comas">
</div>
</div>
<div class="form-help">Selecciona uno o varios destinatarios guardados, o ingresa IDs manualmente separados por comas.</div>
</div>
<div class="form-group">
<label for="plantilla_id">Cargar Plantilla (Opcional)</label>
<select id="plantilla_id" class="form-control" onchange="loadTemplate(this.value)">
<option value="">-- Seleccionar Plantilla --</option>
<?php foreach ($plantillas as $plantilla): ?>
<option value="<?php echo htmlspecialchars($plantilla['id']); ?>">
<?php echo htmlspecialchars($plantilla['nombre']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="contenido">Contenido del Mensaje *</label>
<div id="translation-buttons" class="translation-buttons" style="margin-bottom: 10px; display: flex; gap: 5px;">
<!-- Los botones se cargarán dinámicamente con JavaScript -->
</div>
<button type="button" onclick="openGallery()" class="btn btn-success" style="margin-bottom: 10px; padding: 5px 10px; font-size: 12px;">
<i class="fas fa-images"></i> Insertar Imagen
</button>
<textarea id="contenido" name="contenido" class="form-control" rows="10" required></textarea>
</div>
<div class="form-group">
<label>Tipo de Envío *</label>
<div>
<input type="radio" id="send_immediate" name="send_type" value="inmediato" checked>
<label for="send_immediate">Inmediato</label>
<input type="radio" id="send_scheduled" name="send_type" value="programado" class="ml-3">
<label for="send_scheduled">Programado</label>
<input type="radio" id="send_recurring" name="send_type" value="recurrente" class="ml-3">
<label for="send_recurring">Recurrente</label>
</div>
</div>
<div id="scheduled_options" class="form-group" style="display: none;">
<label for="schedule_datetime">Fecha y Hora de Envío *</label>
<input type="datetime-local" id="schedule_datetime" name="schedule_datetime" class="form-control">
</div>
<div id="recurring_options" class="form-group" style="display: none;">
<label for="recurrence_frequency">Frecuencia de Recurrencia *</label>
<select id="recurrence_frequency" name="recurrence_frequency" class="form-control">
<option value="diario">Diario</option>
<option value="semanal">Semanal</option>
<option value="mensual">Mensual</option>
</select>
<div id="recurring_details" style="margin-top: 15px;">
<!-- Detalles específicos de recurrencia (día de la semana, día del mes) -->
<div id="weekly_options" style="display: none;">
<label for="recurrence_day_of_week">Día de la Semana</label>
<select id="recurrence_day_of_week" name="recurrence_day_of_week" class="form-control">
<option value="1">Lunes</option>
<option value="2">Martes</option>
<option value="3">Miércoles</option>
<option value="4">Jueves</option>
<option value="5">Viernes</option>
<option value="6">Sábado</option>
<option value="7">Domingo</option>
</select>
</div>
<div id="monthly_options" style="display: none;">
<label for="recurrence_day_of_month">Día del Mes</label>
<input type="number" id="recurrence_day_of_month" name="recurrence_day_of_month" class="form-control" min="1" max="31">
</div>
<label for="recurrence_time">Hora de Envío</label>
<input type="time" id="recurrence_time" name="recurrence_time" class="form-control">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="btnEnviar">
<i class="fas fa-paper-plane"></i> Enviar Ahora
</button>
<button type="button" class="btn btn-secondary" onclick="previewMessage()">
<i class="fas fa-eye"></i> Vista Previa
</button>
</div>
</form>
</div>
</div>
<!-- Modal Galería -->
<div id="galleryModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeGallery()">&times;</span>
<h2>Galería de Imágenes</h2>
<div class="gallery-grid" id="galleryGrid"></div>
</div>
</div>
<!-- Templates Data Hidden -->
<script>
const templates = <?php echo json_encode($plantillas); ?>;
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
// Función para cargar los idiomas activos
function loadActiveLanguages() {
return fetch('/shared/languages/get_active.php')
.then(response => response.json())
.then(data => {
if (data.success && data.languages) {
return data.languages;
}
return [];
})
.catch(error => {
console.error('Error cargando idiomas:', error);
return [];
});
}
// Función para crear botones de traducción
function createTranslationButtons(languages) {
const container = $('#translation-buttons');
container.empty();
languages.forEach(lang => {
if (lang.codigo !== 'es') { // No mostramos el botón para español (idioma original)
const button = $(`
<button type="button" class="btn btn-sm btn-outline-secondary translate-btn"
data-lang="${lang.codigo}"
title="Traducir a ${lang.nombre}">
<span class="flag-icon flag-icon-${lang.bandera || 'globe'}"></span>
</button>
`);
container.append(button);
}
});
// Manejador de eventos para los botones de traducción
$('.translate-btn').on('click', async function() {
const targetLang = $(this).data('lang');
const content = $('#contenido').val();
if (!content) {
alert('Por favor, ingrese un mensaje para traducir.');
return;
}
try {
const response = await fetch('/shared/translations/translate.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: content,
target: targetLang,
source: 'es' // Asumimos que el texto original está en español
})
});
const data = await response.json();
if (data.success) {
// Agregar la traducción al final del contenido
const translatedText = `\n\n--- Traducción a ${targetLang.toUpperCase()} ---\n${data.translatedText}`;
$('#contenido').val(content + translatedText);
// Eliminar el botón de traducción
$(this).remove();
} else {
alert('Error al traducir: ' + (data.error || 'Error desconocido'));
}
} catch (error) {
console.error('Error al traducir:', error);
alert('Error al conectar con el servicio de traducción.');
}
});
}
// Cargar los botones de traducción al iniciar
$(document).ready(function() {
loadActiveLanguages().then(languages => {
createTranslationButtons(languages);
});
$('#contenido').summernote({
height: 300,
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['insert', ['link']],
['view', ['fullscreen', 'codeview', 'help']]
]
});
// Inicializar Select2 para selección múltiple
$('#destinatario_select').select2({
placeholder: "-- Seleccionar Destinatarios Guardados --",
allowClear: true // Permite deseleccionar
});
// Sincronizar select con input manual
$('#destinatario_select').on('change', function() {
const selectedIds = $(this).val(); // Array de IDs del select2
const manualIds = $('#destinatario_manual').val().split(',').map(id => id.trim()).filter(id => id !== '');
// Combinar y eliminar duplicados
const combinedIds = [...new Set([...selectedIds, ...manualIds])];
// Actualizar el campo manual para reflejar todas las selecciones y entradas manuales
$('#destinatario_manual').val(combinedIds.join(', '));
});
$('#destinatario_manual').on('input', function() {
const manualIds = $(this).val().split(',').map(id => id.trim()).filter(id => id !== '');
const selectedIds = $('#destinatario_select').val(); // IDs del select2
// Asegurarse de que el select2 no se deseleccione si se añade manualmente
// Esto es complejo si se quiere mantener el estado exacto en ambos sentidos.
// Para simplificar, solo aseguramos que el input manual tenga todos los IDs.
const combinedIds = [...new Set([...manualIds, ...selectedIds])];
// Intentar seleccionar en select2 lo que está en manual si existe
$('#destinatario_select').val(manualIds).trigger('change');
});
});
function loadTemplate(id) {
if (!id) return;
const template = templates.find(t => t.id == id);
if (template) {
if (confirm('¿Reemplazar el contenido actual con la plantilla?')) {
$('#contenido').summernote('code', template.contenido);
}
}
}
function openGallery() {
$('#galleryModal').show();
loadGalleryImages();
}
function closeGallery() {
$('#galleryModal').hide();
}
async function loadGalleryImages() {
try {
const response = await fetch('/gallery/api/list.php');
const data = await response.json();
const grid = document.getElementById('galleryGrid');
if (data.success && data.images.length > 0) {
grid.innerHTML = data.images.map(img => `
<div class="gallery-item" onclick="insertImage('${img.url}')">
<img src="${img.url_thumbnail}" alt="${img.nombre_original}">
</div>
`).join('');
} else {
grid.innerHTML = '<p>No hay imágenes.</p>';
}
} catch (e) {
console.error(e);
}
}
function insertImage(url) {
const fullUrl = window.location.origin + url;
$('#contenido').summernote('insertImage', fullUrl);
closeGallery();
}
function previewMessage() {
const content = $('#contenido').summernote('code');
const win = window.open('', 'Preview', 'width=800,height=600');
win.document.write('<div style="padding:20px;font-family:sans-serif;">' + content + '</div>');
}
// Cerrar modal click fuera
window.onclick = function(event) {
if (event.target == document.getElementById('galleryModal')) {
closeGallery();
}
}
// Lógica para mostrar/ocultar campos de programación/recurrencia
const sendTypeRadios = document.querySelectorAll('input[name="send_type"]');
const scheduledOptions = document.getElementById('scheduled_options');
const recurringOptions = document.getElementById('recurring_options');
const recurrenceFrequency = document.getElementById('recurrence_frequency');
const weeklyOptions = document.getElementById('weekly_options');
const monthlyOptions = document.getElementById('monthly_options');
function toggleSendTypeOptions() {
const selectedSendType = document.querySelector('input[name="send_type"]:checked').value;
scheduledOptions.style.display = 'none';
recurringOptions.style.display = 'none';
if (selectedSendType === 'programado') {
scheduledOptions.style.display = 'block';
} else if (selectedSendType === 'recurrente') {
recurringOptions.style.display = 'block';
toggleRecurringDetails(); // Mostrar detalles específicos al cargar
}
}
function toggleRecurringDetails() {
const selectedFrequency = recurrenceFrequency.value;
weeklyOptions.style.display = 'none';
monthlyOptions.style.display = 'none';
if (selectedFrequency === 'semanal') {
weeklyOptions.style.display = 'block';
} else if (selectedFrequency === 'mensual') {
monthlyOptions.style.display = 'block';
}
}
// Inicializar al cargar la página
toggleSendTypeOptions();
// Escuchar cambios en los tipos de envío
sendTypeRadios.forEach(radio => {
radio.addEventListener('change', toggleSendTypeOptions);
});
// Escuchar cambios en la frecuencia de recurrencia
recurrenceFrequency.addEventListener('change', toggleRecurringDetails);
$('#messageForm').on('submit', async function(e) {
e.preventDefault();
const selectedDestinatarios = $('#destinatario_select').val() || []; // Array de IDs del select2
const manualDestinatarios = $('#destinatario_manual').val()
.split(',')
.map(id => id.trim())
.filter(id => id !== '');
// Combinar y eliminar duplicados de ambos orígenes
const destinatarios = [...new Set([...selectedDestinatarios, ...manualDestinatarios])];
const contenido = $('#contenido').summernote('code');
const sendType = document.querySelector('input[name="send_type"]:checked').value;
let scheduleData = {};
if (destinatarios.length === 0 || !contenido) {
alert('Por favor selecciona al menos un destinatario y escribe un mensaje.');
return;
}
if (sendType === 'programado') {
const scheduleDatetime = $('#schedule_datetime').val();
if (!scheduleDatetime) {
alert('Por favor, selecciona la fecha y hora de envío programado.');
return;
}
scheduleData = {
fecha_envio: scheduleDatetime
};
} else if (sendType === 'recurrente') {
const recurrenceFrequencyVal = $('#recurrence_frequency').val();
const recurrenceTime = $('#recurrence_time').val();
if (!recurrenceFrequencyVal || !recurrenceTime) {
alert('Por favor, completa la frecuencia y hora de envío recurrente.');
return;
}
scheduleData = {
frecuencia: recurrenceFrequencyVal,
hora_envio: recurrenceTime
};
if (recurrenceFrequencyVal === 'semanal') {
const dayOfWeek = $('#recurrence_day_of_week').val();
if (!dayOfWeek) {
alert('Por favor, selecciona el día de la semana para el envío recurrente.');
return;
}
scheduleData.dia_semana = dayOfWeek;
} else if (recurrenceFrequencyVal === 'mensual') {
const dayOfMonth = $('#recurrence_day_of_month').val();
if (!dayOfMonth || dayOfMonth < 1 || dayOfMonth > 31) {
alert('Por favor, ingresa un día válido del mes (1-31) para el envío recurrente.');
return;
}
scheduleData.dia_mes = dayOfMonth;
}
}
if (!confirm(`¿Enviar mensaje (${sendType}) a ${destinatarios.length} destinatario(s)?`)) return;
$('#btnEnviar').prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Enviando...');
try {
const response = await fetch('/discord/api/messages/send.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
destinatario_id: destinatarios, // Ahora puede ser un array
contenido: contenido,
tipo_envio: sendType,
...scheduleData // Añadir datos de programación/recurrencia
})
});
const result = await response.json();
if (result.success) {
alert('¡Mensaje enviado correctamente!');
window.location.href = '/discord/views/messages/sent.php';
} else {
alert('Error: ' + (result.error || 'Error desconocido'));
}
} catch (error) {
console.error(error);
alert('Error de conexión');
} finally {
$('#btnEnviar').prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Enviar Ahora');
}
});
</script>
</body>
</html>