Bot Discord - Commit completo con todos los cambios
This commit is contained in:
66
includes/Translate.php
Executable file
66
includes/Translate.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
// includes/Translate.php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
class Translate {
|
||||
private $client;
|
||||
|
||||
public function __construct() {
|
||||
$this->client = new Client([
|
||||
'base_uri' => LIBRETRANSLATE_URL,
|
||||
'timeout' => 10.0,
|
||||
'verify' => false
|
||||
]);
|
||||
}
|
||||
|
||||
public function translate($text, $targetLang, $sourceLang = 'auto') {
|
||||
try {
|
||||
$response = $this->client->post('/translate', [
|
||||
'json' => [
|
||||
'q' => $text,
|
||||
'source' => $sourceLang,
|
||||
'target' => $targetLang,
|
||||
'format' => 'text'
|
||||
]
|
||||
]);
|
||||
|
||||
$body = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
if (isset($body['translatedText'])) {
|
||||
return $body['translatedText'];
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (RequestException $e) {
|
||||
error_log("Error en traducción: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function translateBatch($texts, $targetLang, $sourceLang = 'auto') {
|
||||
try {
|
||||
$response = $this->client->post('/translate', [
|
||||
'json' => [
|
||||
'q' => $texts,
|
||||
'source' => $sourceLang,
|
||||
'target' => $targetLang,
|
||||
'format' => 'text'
|
||||
]
|
||||
]);
|
||||
|
||||
$body = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
// LibreTranslate devuelve un array de objetos con la clave 'translatedText'
|
||||
if (isset($body['translatedText']) && is_array($body['translatedText'])) {
|
||||
return $body['translatedText'];
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
error_log("Error en traducción por lotes: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
35
includes/activity_logger.php
Executable file
35
includes/activity_logger.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
function log_activity(int $userId, string $action, ?string $details = null): void
|
||||
{
|
||||
global $pdo;
|
||||
|
||||
// Debugging: Check if $pdo is a valid PDO object
|
||||
if (!($pdo instanceof PDO)) {
|
||||
error_log("Error logging activity: $pdo is not a valid PDO object. Type: " . gettype($pdo));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch username from session or database if not available
|
||||
$username = $_SESSION['username'] ?? 'Unknown';
|
||||
if ($username === 'Unknown' && $userId > 0) {
|
||||
$stmt = $pdo->prepare("SELECT username FROM users WHERE id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($user) {
|
||||
$username = $user['username'];
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO activity_log (user_id, username, action, details, timestamp)
|
||||
VALUES (?, ?, ?, ?, NOW())"
|
||||
);
|
||||
$stmt->execute([$userId, $username, $action, $details]);
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error logging activity: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
49
includes/auth.php
Executable file
49
includes/auth.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db.php';
|
||||
require_once 'activity_logger.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// CSRF token validation
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
|
||||
header('Location: ../login.php?error=csrf_error');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Unset the token so it can't be used again
|
||||
// unset($_SESSION['csrf_token']); // Keep token for re-attempts on failed login
|
||||
|
||||
if (!isset($_POST['username']) || !isset($_POST['password'])) {
|
||||
header('Location: ../login.php?error=missing_fields');
|
||||
exit();
|
||||
}
|
||||
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
session_regenerate_id(true);
|
||||
log_activity($user['id'], 'User Login', 'User ' . $user['username'] . ' logged in.');
|
||||
session_write_close();
|
||||
header('Location: ../index.php');
|
||||
exit();
|
||||
} else {
|
||||
header('Location: ../login.php?error=invalid_credentials');
|
||||
exit();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Log the error instead of showing it to the user
|
||||
error_log('Authentication error: ' . $e->getMessage());
|
||||
header('Location: ../login.php?error=db_error');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
?>
|
||||
126
includes/db.php
Executable file
126
includes/db.php
Executable file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
// Establecer la zona horaria predeterminada
|
||||
date_default_timezone_set('America/Mexico_City');
|
||||
|
||||
/**
|
||||
* Clase para manejar la conexión a la base de datos con reconexión automática
|
||||
*/
|
||||
class DatabaseConnection {
|
||||
private static $instance = null;
|
||||
private $pdo = null;
|
||||
private $config = [];
|
||||
|
||||
private function __construct() {
|
||||
$this->config = [
|
||||
'host' => $_ENV['DB_HOST'] ?? 'localhost',
|
||||
'port' => $_ENV['DB_PORT'] ?? '3306',
|
||||
'name' => $_ENV['DB_NAME'] ?? 'bot',
|
||||
'user' => $_ENV['DB_USER'] ?? 'nickpons666',
|
||||
'pass' => $_ENV['DB_PASS'] ?? 'MiPo6425@@',
|
||||
'charset' => 'utf8mb4',
|
||||
'timeout' => 30, // Tiempo de espera de conexión en segundos
|
||||
'reconnect_attempts' => 3, // Número de intentos de reconexión
|
||||
'reconnect_delay' => 1, // Tiempo de espera entre reconexiones en segundos
|
||||
];
|
||||
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
// Verificar si la conexión sigue activa
|
||||
try {
|
||||
$this->pdo->query('SELECT 1');
|
||||
return $this->pdo;
|
||||
} catch (PDOException $e) {
|
||||
// Si la conexión se perdió, intentar reconectar
|
||||
error_log("La conexión a la base de datos se perdió. Intentando reconectar...");
|
||||
$this->connect();
|
||||
return $this->pdo;
|
||||
}
|
||||
}
|
||||
|
||||
private function connect() {
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;port=%s;dbname=%s;charset=%s',
|
||||
$this->config['host'],
|
||||
$this->config['port'],
|
||||
$this->config['name'],
|
||||
$this->config['charset']
|
||||
);
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_TIMEOUT => $this->config['timeout'],
|
||||
PDO::ATTR_PERSISTENT => false, // No usar conexiones persistentes para evitar problemas
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
|
||||
];
|
||||
|
||||
$attempts = 0;
|
||||
$lastException = null;
|
||||
|
||||
while ($attempts < $this->config['reconnect_attempts']) {
|
||||
try {
|
||||
$this->pdo = new PDO(
|
||||
$dsn,
|
||||
$this->config['user'],
|
||||
$this->config['pass'],
|
||||
$options
|
||||
);
|
||||
|
||||
// Configuración adicional de la conexión
|
||||
$this->pdo->exec("SET time_zone = '-06:00';");
|
||||
$this->pdo->exec("SET SESSION wait_timeout=28800;"); // 8 horas
|
||||
$this->pdo->exec("SET SESSION interactive_timeout=28800;"); // 8 horas
|
||||
|
||||
error_log("Conexión a la base de datos establecida correctamente.");
|
||||
return;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$lastException = $e;
|
||||
$attempts++;
|
||||
error_log(sprintf(
|
||||
"Intento de conexión %d fallido: %s. Reintentando en %d segundos...",
|
||||
$attempts,
|
||||
$e->getMessage(),
|
||||
$this->config['reconnect_delay']
|
||||
));
|
||||
|
||||
if ($attempts < $this->config['reconnect_attempts']) {
|
||||
sleep($this->config['reconnect_delay']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si llegamos aquí, todos los intentos fallaron
|
||||
error_log("No se pudo establecer la conexión después de {$this->config['reconnect_attempts']} intentos.");
|
||||
throw $lastException;
|
||||
}
|
||||
}
|
||||
|
||||
// Crear una instancia de la conexión
|
||||
try {
|
||||
$pdo = DatabaseConnection::getInstance()->getConnection();
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error crítico de conexión a la base de datos: " . $e->getMessage());
|
||||
// No usar die() aquí porque mata a los workers - dejar que el código maneje el error
|
||||
// Para scripts web, el error se mostrará en el log y el script continuará
|
||||
// Para workers, pueden manejar la excepción y reintentar
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
// Solo para contexto web
|
||||
die("Error de conexión a la base de datos. Por favor, inténtalo de nuevo más tarde.");
|
||||
}
|
||||
// Para CLI (workers), lanzar la excepción para que el worker la maneje
|
||||
throw $e;
|
||||
}
|
||||
?>
|
||||
72
includes/discord_actions.php
Executable file
72
includes/discord_actions.php
Executable file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/../src/DiscordSender.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['action'])) {
|
||||
header('Location: ../sent_messages.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$action = $_POST['action'];
|
||||
|
||||
if ($action === 'delete_message') {
|
||||
if (!isset($_POST['sent_message_id'], $_POST['platform_message_id'], $_POST['channel_id'])) {
|
||||
header('Location: ../sent_messages.php?error=missing_data');
|
||||
exit();
|
||||
}
|
||||
|
||||
$sentMessageId = $_POST['sent_message_id'];
|
||||
$discordMessageIdsJson = $_POST['platform_message_id'];
|
||||
$channelId = $_POST['channel_id']; // The channel where the message was sent
|
||||
|
||||
$discordMessageIds = json_decode($discordMessageIdsJson, true);
|
||||
|
||||
// If decoding fails or the result is not an array, treat it as a single ID
|
||||
if (json_last_error() !== JSON_ERROR_NONE || !is_array($discordMessageIds)) {
|
||||
$discordMessageIds = [$discordMessageIdsJson];
|
||||
}
|
||||
|
||||
$discordSender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
|
||||
try {
|
||||
// 1. Attempt to delete from Discord
|
||||
$all_deleted = true;
|
||||
foreach ($discordMessageIds as $discordMessageId) {
|
||||
// Skip if the ID is empty or invalid
|
||||
if (empty($discordMessageId) || !is_numeric($discordMessageId)) {
|
||||
error_log("Skipping invalid message chunk ID: " . var_export($discordMessageId, true));
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
error_log("Attempting to delete message chunk ID: {$discordMessageId} in channel {$channelId}");
|
||||
$discordSender->deleteMessage($channelId, $discordMessageId);
|
||||
usleep(500000); // Wait 500ms to avoid rate limiting
|
||||
} catch (Exception $e) {
|
||||
error_log("Failed to delete message chunk {$discordMessageId}: " . $e->getMessage());
|
||||
$all_deleted = false; // Mark that at least one failed
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If all chunks were deleted (or if there was only one), delete from our database
|
||||
if ($all_deleted) {
|
||||
$stmt = $pdo->prepare("DELETE FROM sent_messages WHERE id = ?");
|
||||
$stmt->execute([$sentMessageId]);
|
||||
header('Location: ../sent_messages.php?success=deleted&platform=Discord');
|
||||
} else {
|
||||
// If some failed, we don't delete the entry, so it can be retried.
|
||||
// We could also add more sophisticated logic here, like storing partial success.
|
||||
header('Location: ../sent_messages.php?error=delete_failed_partial&platform=Discord');
|
||||
}
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Discord message deletion failed: " . $e->getMessage());
|
||||
header('Location: ../sent_messages.php?error=delete_failed&platform=Discord&message=' . urlencode($e->getMessage()));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback redirect
|
||||
header('Location: ../sent_messages.php');
|
||||
?>
|
||||
16
includes/emojis.php
Executable file
16
includes/emojis.php
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* ARCHIVO BRIDGE - Migración Progresiva
|
||||
*
|
||||
* Este archivo mantiene compatibilidad con código existente
|
||||
* mientras redirige a la nueva ubicación del helper.
|
||||
*
|
||||
* Nueva ubicación: /common/helpers/emojis.php
|
||||
* Fecha de migración: 2025-11-25
|
||||
*/
|
||||
|
||||
// Cargar el archivo desde la nueva ubicación
|
||||
require_once __DIR__ . '/../common/helpers/emojis.php';
|
||||
|
||||
// Este archivo puede ser eliminado cuando toda la migración esté completa
|
||||
// y todas las referencias apunten directamente a common/helpers/emojis.php
|
||||
56
includes/error_handler.php
Executable file
56
includes/error_handler.php
Executable file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// Configuración de manejo de errores
|
||||
function customErrorHandler($errno, $errstr, $errfile, $errline) {
|
||||
$logFile = __DIR__ . '/../logs/php_errors.log';
|
||||
|
||||
// Definir los tipos de error que queremos registrar
|
||||
$error_types = [
|
||||
E_ERROR => 'ERROR',
|
||||
E_WARNING => 'WARNING',
|
||||
E_PARSE => 'PARSING ERROR',
|
||||
E_NOTICE => 'NOTICE',
|
||||
E_CORE_ERROR => 'CORE ERROR',
|
||||
E_CORE_WARNING => 'CORE WARNING',
|
||||
E_COMPILE_ERROR => 'COMPILE ERROR',
|
||||
E_COMPILE_WARNING => 'COMPILE WARNING',
|
||||
E_USER_ERROR => 'USER ERROR',
|
||||
E_USER_WARNING => 'USER WARNING',
|
||||
E_USER_NOTICE => 'USER NOTICE',
|
||||
E_STRICT => 'STRICT NOTICE',
|
||||
E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR',
|
||||
E_DEPRECATED => 'DEPRECATED',
|
||||
E_USER_DEPRECATED => 'USER DEPRECATED'
|
||||
];
|
||||
|
||||
$error_type = $error_types[$errno] ?? 'UNKNOWN';
|
||||
$error_message = "[" . date('Y-m-d H:i:s') . "] $error_type: $errstr in $errfile on line $errline\n";
|
||||
|
||||
// Escribir en el archivo de log
|
||||
error_log($error_message, 3, $logFile);
|
||||
|
||||
// No ejecutar el gestor de errores interno de PHP
|
||||
return true;
|
||||
}
|
||||
|
||||
// Función para registrar excepciones no capturadas
|
||||
function customExceptionHandler($exception) {
|
||||
$logFile = __DIR__ . '/../logs/php_errors.log';
|
||||
$error_message = "[" . date('Y-m-d H:i:s') . "] EXCEPTION: " . $exception->getMessage() . " in " .
|
||||
$exception->getFile() . " on line " . $exception->getLine() . "\n";
|
||||
error_log($error_message, 3, $logFile);
|
||||
|
||||
// Mostrar un mensaje genérico al usuario
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
echo "Ha ocurrido un error inesperado. El administrador ha sido notificado.";
|
||||
}
|
||||
|
||||
// Establecer manejadores de errores
|
||||
set_error_handler('customErrorHandler');
|
||||
set_exception_handler('customExceptionHandler');
|
||||
|
||||
// Mostrar errores en pantalla solo en entorno de desarrollo
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', __DIR__ . '/../logs/php_errors.log');
|
||||
134
includes/get_gallery.php
Executable file
134
includes/get_gallery.php
Executable file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Directorio donde se almacenan las imágenes
|
||||
$uploadDir = __DIR__ . '/../uploads/';
|
||||
$baseUrl = '/bot/uploads/';
|
||||
|
||||
$response = [
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'images' => []
|
||||
];
|
||||
|
||||
try {
|
||||
// Verificar si el directorio existe
|
||||
if (!is_dir($uploadDir)) {
|
||||
throw new Exception('El directorio de imágenes no existe');
|
||||
}
|
||||
|
||||
// Obtener archivos del directorio
|
||||
$files = scandir($uploadDir);
|
||||
$imageFiles = array_filter($files, function($file) use ($uploadDir) {
|
||||
// Solo archivos de imagen
|
||||
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||
return in_array($ext, $imageExtensions) && is_file($uploadDir . $file);
|
||||
});
|
||||
|
||||
// Construir la respuesta con las imágenes
|
||||
foreach ($imageFiles as $file) {
|
||||
$filePath = $uploadDir . $file;
|
||||
$fileUrl = $baseUrl . $file;
|
||||
|
||||
// Crear miniatura si no existe
|
||||
$thumbnailPath = $uploadDir . 'thumbs/' . $file;
|
||||
$thumbnailUrl = $baseUrl . 'thumbs/' . $file;
|
||||
|
||||
if (!file_exists($thumbnailPath)) {
|
||||
// Crear directorio de miniaturas si no existe
|
||||
if (!is_dir(dirname($thumbnailPath))) {
|
||||
mkdir(dirname($thumbnailPath), 0755, true);
|
||||
}
|
||||
|
||||
// Crear miniatura
|
||||
createThumbnail($filePath, $thumbnailPath, 200, 200);
|
||||
}
|
||||
|
||||
$response['images'][] = [
|
||||
'name' => $file,
|
||||
'url' => $fileUrl,
|
||||
'thumbnail' => file_exists($thumbnailPath) ? $thumbnailUrl : $fileUrl,
|
||||
'size' => filesize($filePath),
|
||||
'mtime' => filemtime($filePath)
|
||||
];
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
$response['message'] = count($response['images']) . ' imágenes encontradas';
|
||||
|
||||
} catch (Exception $e) {
|
||||
$response['message'] = 'Error al cargar la galería: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
|
||||
/**
|
||||
* Crea una miniatura de una imagen
|
||||
*/
|
||||
function createThumbnail($sourcePath, $destPath, $maxWidth, $maxHeight) {
|
||||
$sourceInfo = getimagesize($sourcePath);
|
||||
if (!$sourceInfo) return false;
|
||||
|
||||
list($origWidth, $origHeight, $type) = $sourceInfo;
|
||||
|
||||
// Calcular nuevas dimensiones manteniendo la relación de aspecto
|
||||
$ratio = $origWidth / $origHeight;
|
||||
if ($maxWidth / $maxHeight > $ratio) {
|
||||
$maxWidth = $maxHeight * $ratio;
|
||||
} else {
|
||||
$maxHeight = $maxWidth / $ratio;
|
||||
}
|
||||
|
||||
// Crear imagen de origen
|
||||
switch ($type) {
|
||||
case IMAGETYPE_JPEG:
|
||||
$source = imagecreatefromjpeg($sourcePath);
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
$source = imagecreatefrompng($sourcePath);
|
||||
break;
|
||||
case IMAGETYPE_GIF:
|
||||
$source = imagecreatefromgif($sourcePath);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$source) return false;
|
||||
|
||||
// Crear imagen de destino
|
||||
$thumb = imagecreatetruecolor($maxWidth, $maxHeight);
|
||||
|
||||
// Preservar transparencia para PNG y GIF
|
||||
if ($type == IMAGETYPE_PNG || $type == IMAGETYPE_GIF) {
|
||||
imagecolortransparent($thumb, imagecolorallocatealpha($thumb, 0, 0, 0, 127));
|
||||
imagealphablending($thumb, false);
|
||||
imagesavealpha($thumb, true);
|
||||
}
|
||||
|
||||
// Redimensionar
|
||||
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $maxWidth, $maxHeight, $origWidth, $origHeight);
|
||||
|
||||
// Guardar miniatura
|
||||
switch ($type) {
|
||||
case IMAGETYPE_JPEG:
|
||||
imagejpeg($thumb, $destPath, 90);
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
imagepng($thumb, $destPath, 9);
|
||||
break;
|
||||
case IMAGETYPE_GIF:
|
||||
imagegif($thumb, $destPath);
|
||||
break;
|
||||
}
|
||||
|
||||
// Liberar memoria
|
||||
imagedestroy($source);
|
||||
imagedestroy($thumb);
|
||||
|
||||
return true;
|
||||
}
|
||||
10
includes/logger.php
Executable file
10
includes/logger.php
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// Función de depuración personalizada
|
||||
if (!function_exists('custom_log')) {
|
||||
function custom_log($message) {
|
||||
$logFile = __DIR__ . '/../logs/custom_debug.log'; // Adjust path as needed
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
file_put_contents('php://stderr', "[$timestamp] $message\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
?>
|
||||
361
includes/message_handler.php
Executable file
361
includes/message_handler.php
Executable file
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
// Configurar zona horaria
|
||||
date_default_timezone_set('America/Mexico_City');
|
||||
|
||||
// Incluir el helper de programación
|
||||
require_once __DIR__ . '/schedule_helpers.php';
|
||||
|
||||
// Configuración de logs
|
||||
$logFile = dirname(__DIR__) . '/logs/discord_api.log';
|
||||
|
||||
// Crear el directorio de logs si no existe
|
||||
if (!file_exists(dirname($logFile))) {
|
||||
mkdir(dirname($logFile), 0755, true);
|
||||
}
|
||||
|
||||
// Incluir archivos necesarios
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/activity_logger.php';
|
||||
|
||||
$submitAction = $_POST['submit'] ?? '';
|
||||
|
||||
// --- Special handler for sending from a template ---
|
||||
if ($submitAction === 'send_from_template') {
|
||||
$template_id = $_POST['recurrent_message_id'] ?? 0;
|
||||
$user_ids = $_POST['recipientId_user'] ?? [];
|
||||
$scheduleType = $_POST['scheduleType'] ?? 'now';
|
||||
$scheduleDateTime = $_POST['scheduleDateTime'] ?? null;
|
||||
$userId = $_SESSION['user_id'];
|
||||
|
||||
if (empty($template_id) || empty($user_ids)) {
|
||||
header("Location: ../enviar_plantilla.php?error=missing_fields");
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
// Fetch the message content from the template
|
||||
$stmt = $pdo->prepare("SELECT message_content FROM recurrent_messages WHERE id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$template) {
|
||||
throw new Exception("La plantilla seleccionada no existe.");
|
||||
}
|
||||
$messageContent = $template['message_content'];
|
||||
|
||||
// 1. Insert the main message content into the messages table
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, content) VALUES (?, ?)");
|
||||
$stmt->execute([$userId, $messageContent]);
|
||||
$messageId = $pdo->lastInsertId();
|
||||
|
||||
// 2. Determine the send time
|
||||
$sendTime = ($scheduleType === 'later')
|
||||
? (new DateTime($scheduleDateTime, new DateTimeZone('America/Mexico_City')))->format('Y-m-d H:i:s')
|
||||
: date('Y-m-d H:i:s');
|
||||
|
||||
// 3. Create a schedule for each selected user
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO schedules (message_id, recipient_id, send_time, status, is_recurring, recurring_days, recurring_time)
|
||||
VALUES (?, ?, ?, 'pending', 0, NULL, NULL)"
|
||||
);
|
||||
|
||||
foreach ($user_ids as $user_id) {
|
||||
$stmt->execute([$messageId, $user_id, $sendTime]);
|
||||
log_activity($userId, 'Message Scheduled from Template', 'Schedule for user ID: ' . $user_id);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// If sending now, trigger the queue processor
|
||||
if ($scheduleType === 'now') {
|
||||
$phpPath = PHP_BINARY ?: 'php';
|
||||
$scriptPath = dirname(__DIR__) . '/process_queue.php';
|
||||
$logPath = dirname(__DIR__) . '/logs/process_queue_manual.log';
|
||||
$command = sprintf('%s %s >> %s 2>&1 &', escapeshellarg($phpPath), escapeshellarg($scriptPath), escapeshellarg($logPath));
|
||||
shell_exec($command);
|
||||
}
|
||||
|
||||
header("Location: ../scheduled_messages.php?success=template_sent");
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
error_log("TemplateHandler Error: " . $e->getMessage() . "\n", 3, dirname(__DIR__) . '/logs/discord_api.log');
|
||||
header("Location: ../enviar_plantilla.php?error=dberror");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Regular message handling starts here
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$content = $_POST['messageContent'] ?? '';
|
||||
error_log("[DEBUG] Message content in message_handler.php: " . substr($content, 0, 500)); // Log first 500 chars
|
||||
$platform = $_POST['platform'] ?? ''; // 'discord' or 'telegram'
|
||||
|
||||
// Obtener canales y usuarios seleccionados (pueden ser arrays JSON)
|
||||
$channelIds = json_decode($_POST['channelIds'] ?? '[]', true);
|
||||
$userIds = json_decode($_POST['userIds'] ?? '[]', true);
|
||||
|
||||
// Si no hay canales ni usuarios seleccionados, verificar el campo legacy recipientId
|
||||
if (empty($channelIds) && empty($userIds) && !empty($_POST['recipientId'])) {
|
||||
// Para compatibilidad con el código existente
|
||||
$channelIds = [$_POST['recipientId']];
|
||||
}
|
||||
|
||||
// Combinar todos los IDs de destinatarios y eliminar valores vacíos
|
||||
$allRecipientIds = array_filter(array_merge($channelIds, $userIds));
|
||||
|
||||
// Debug: Registrar los destinatarios recibidos
|
||||
error_log("Channel IDs: " . print_r($channelIds, true));
|
||||
error_log("User IDs: " . print_r($userIds, true));
|
||||
error_log("All Recipient IDs: " . print_r($allRecipientIds, true));
|
||||
|
||||
// Validar que haya al menos un destinatario
|
||||
if (empty($allRecipientIds)) {
|
||||
header('Location: ../create_message.php?error=no_recipients');
|
||||
exit();
|
||||
}
|
||||
$scheduleId = $_POST['schedule_id'] ?? null;
|
||||
$submitAction = $_POST['submit'] ?? 'send';
|
||||
$isEditing = ($submitAction === 'update' && !empty($scheduleId));
|
||||
|
||||
// Basic validation
|
||||
if (empty($content) || empty($platform) || empty(array_filter($allRecipientIds))) {
|
||||
$error_param = 'missing_fields';
|
||||
if (empty($platform)) $error_param = 'missing_platform';
|
||||
if (empty(array_filter($allRecipientIds))) $error_param = 'missing_recipient';
|
||||
|
||||
$error_url = $isEditing
|
||||
? "../create_message.php?action=edit&schedule_id={$scheduleId}&error={$error_param}"
|
||||
: "../create_message.php?error={$error_param}";
|
||||
header("Location: {$error_url}");
|
||||
exit();
|
||||
}
|
||||
|
||||
$scheduleType = $_POST['scheduleType'] ?? 'now';
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// --- DEBUG LOGGING ---
|
||||
$debugLogFile = dirname(__DIR__) . '/logs/schedule_debug.log';
|
||||
$logEntry = "[" . date('Y-m-d H:i:s') . "] --- INICIO DE PETICIÓN ---\n";
|
||||
$logEntry .= "ACTION: {$submitAction}, SCHEDULE_ID: " . ($scheduleId ?? 'null') . "\n";
|
||||
$logEntry .= "PLATFORM: {$platform}\n";
|
||||
$logEntry .= "CHANNEL_IDS: " . (is_array($channelIds) ? implode(', ', $channelIds) : $channelIds) . "\n";
|
||||
$logEntry .= "USER_IDS: " . (is_array($userIds) ? implode(', ', $userIds) : $userIds) . "\n";
|
||||
$logEntry .= "CONTENT LENGTH: " . strlen($content) . "\n";
|
||||
$logEntry .= "POST DATA: " . json_encode($_POST, JSON_PRETTY_PRINT) . "\n";
|
||||
file_put_contents($debugLogFile, $logEntry, FILE_APPEND);
|
||||
// --- FIN DEBUG LOGGING ---
|
||||
|
||||
// Validar que haya contenido y destinatarios
|
||||
if (empty(trim($content))) {
|
||||
throw new Exception("El contenido del mensaje no puede estar vacío.");
|
||||
}
|
||||
|
||||
if (empty($channelIds) && empty($userIds)) {
|
||||
throw new Exception("Debes seleccionar al menos un canal o usuario como destinatario.");
|
||||
}
|
||||
|
||||
// Combinar todos los IDs de destinatarios
|
||||
$allRecipientIds = array_merge(
|
||||
is_array($channelIds) ? $channelIds : [],
|
||||
is_array($userIds) ? $userIds : []
|
||||
);
|
||||
|
||||
// Validar que todos los destinatarios existan
|
||||
if (!empty($allRecipientIds)) {
|
||||
$placeholders = rtrim(str_repeat('?,', count($allRecipientIds)), ',');
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM recipients WHERE id IN ($placeholders)");
|
||||
$stmt->execute($allRecipientIds);
|
||||
$validRecipients = (int)$stmt->fetchColumn();
|
||||
|
||||
if ($validRecipients !== count($allRecipientIds)) {
|
||||
throw new Exception("Uno o más destinatarios no son válidos.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($submitAction === 'update' && !empty($scheduleId)) {
|
||||
// UPDATE LOGIC
|
||||
$stmt = $pdo->prepare("SELECT message_id FROM schedules WHERE id = ?");
|
||||
$stmt->execute([$scheduleId]);
|
||||
$originalMessage = $stmt->fetch();
|
||||
|
||||
if (!$originalMessage) {
|
||||
throw new Exception("No se encontró la programación original.");
|
||||
}
|
||||
$messageId = $originalMessage['message_id'];
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE messages SET content = ? WHERE id = ?");
|
||||
$stmt->execute([$content, $messageId]);
|
||||
|
||||
$sendTime = null;
|
||||
$isRecurring = 0;
|
||||
$recurringDaysStr = null;
|
||||
$recurringTime = null;
|
||||
$status = 'pending';
|
||||
$stmt = $pdo->prepare("DELETE FROM schedules WHERE message_id = ?");
|
||||
$stmt->execute([$originalMessage['message_id']]);
|
||||
|
||||
// Obtener la configuración de programación
|
||||
$sendTime = null;
|
||||
$status = 'pending';
|
||||
$isRecurring = 0;
|
||||
$recurringDaysStr = null;
|
||||
$recurringTime = null;
|
||||
|
||||
if ($scheduleType === 'later') {
|
||||
$user_timezone = new DateTimeZone('America/Mexico_City');
|
||||
$schedule_dt = DateTime::createFromFormat('Y-m-d\TH:i', $_POST['scheduleDateTime'], $user_timezone);
|
||||
if (!$schedule_dt) {
|
||||
throw new Exception("Formato de fecha/hora de programación no válido.");
|
||||
}
|
||||
$sendTime = $schedule_dt->format('Y-m-d H:i:s');
|
||||
} elseif ($scheduleType === 'recurring') {
|
||||
$isRecurring = 1;
|
||||
$recurringDays = $_POST['recurringDays'] ?? [];
|
||||
sort($recurringDays);
|
||||
$recurringDaysStr = !empty($recurringDays) ? implode(',', $recurringDays) : null;
|
||||
$recurringTime = $_POST['recurringTime'] ?? '00:00';
|
||||
$sendTime = calculateNextSendTime($recurringDays, $recurringTime);
|
||||
} else { // 'now'
|
||||
$sendTime = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// Create new schedules for each recipient
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO schedules (message_id, recipient_id, send_time, status, is_recurring, recurring_days, recurring_time) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
foreach ($allRecipientIds as $recipientId) {
|
||||
$stmt->execute([
|
||||
$originalMessage['message_id'],
|
||||
$recipientId,
|
||||
$sendTime,
|
||||
$status,
|
||||
$isRecurring,
|
||||
$recurringDaysStr,
|
||||
$recurringTime
|
||||
]);
|
||||
log_activity($userId, 'Schedule Recipient Updated', 'Schedule for Message ID: ' . $originalMessage['message_id'] . ' to Recipient ID: ' . $recipientId);
|
||||
}
|
||||
|
||||
// --- DEBUG LOGGING ---
|
||||
$logEntry = "Calculated sendTime for UPDATE: {$sendTime} (Timezone: " . date_default_timezone_get() . ")\n";
|
||||
file_put_contents($debugLogFile, $logEntry, FILE_APPEND);
|
||||
// --- FIN DEBUG LOGGING ---
|
||||
|
||||
$success_param = 'updated';
|
||||
log_activity($userId, 'Message Updated', 'Schedule ID: ' . $scheduleId);
|
||||
|
||||
} else {
|
||||
// CREATE LOGIC
|
||||
$sendTime = null;
|
||||
$status = 'pending';
|
||||
$isRecurring = 0;
|
||||
$recurringDaysStr = null;
|
||||
$recurringTime = null;
|
||||
|
||||
// Determinar la configuración de programación
|
||||
if ($scheduleType === 'later') {
|
||||
$user_timezone = new DateTimeZone('America/Mexico_City');
|
||||
$schedule_dt = DateTime::createFromFormat('Y-m-d\TH:i', $_POST['scheduleDateTime'], $user_timezone);
|
||||
if (!$schedule_dt) {
|
||||
throw new Exception("Formato de fecha/hora de programación no válido.");
|
||||
}
|
||||
$sendTime = $schedule_dt->format('Y-m-d H:i:s');
|
||||
} elseif ($scheduleType === 'recurring') {
|
||||
$isRecurring = 1;
|
||||
$recurringDays = $_POST['recurringDays'] ?? [];
|
||||
sort($recurringDays);
|
||||
$recurringDaysStr = !empty($recurringDays) ? implode(',', $recurringDays) : null;
|
||||
$recurringTime = $_POST['recurringTime'] ?? '00:00';
|
||||
$sendTime = calculateNextSendTime($recurringDays, $recurringTime);
|
||||
} else { // 'now'
|
||||
$sendTime = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// Insertar un solo mensaje
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, content) VALUES (?, ?)");
|
||||
$stmt->execute([$userId, $content]);
|
||||
$messageId = $pdo->lastInsertId();
|
||||
|
||||
// Crear programaciones para cada destinatario
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO schedules (message_id, recipient_id, send_time, status, is_recurring, recurring_days, recurring_time) " .
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
// Combinar canales y usuarios en un solo array
|
||||
$allRecipients = array_merge(
|
||||
is_array($channelIds) ? $channelIds : [],
|
||||
is_array($userIds) ? $userIds : []
|
||||
);
|
||||
|
||||
// Crear una programación para cada destinatario
|
||||
foreach ($allRecipients as $recipientId) {
|
||||
$stmt->execute([
|
||||
$messageId,
|
||||
$recipientId,
|
||||
$sendTime,
|
||||
$status,
|
||||
$isRecurring,
|
||||
$recurringDaysStr,
|
||||
$recurringTime
|
||||
]);
|
||||
$scheduleId = $pdo->lastInsertId();
|
||||
log_activity($userId, 'Message Created', 'Schedule ID: ' . $scheduleId . ' for Recipient ID: ' . $recipientId);
|
||||
}
|
||||
|
||||
$success_param = 'message_created';
|
||||
|
||||
// --- DEBUG LOGGING ---
|
||||
$logEntry = "Calculated sendTime for CREATE: {$sendTime} (Timezone: " . date_default_timezone_get() . ")\n-- FIN DE PETICIÓN --\n\n";
|
||||
file_put_contents($debugLogFile, $logEntry, FILE_APPEND);
|
||||
// --- FIN DEBUG LOGGING ---
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
if ($scheduleType === 'now') {
|
||||
$phpPath = PHP_BINARY ?: 'php';
|
||||
$scriptPath = dirname(__DIR__) . '/process_queue.php';
|
||||
$logPath = dirname(__DIR__) . '/logs/process_queue_manual.log';
|
||||
|
||||
// Asegurarse de que se use el entorno correcto
|
||||
$command = sprintf(
|
||||
'APP_ENVIRONMENT=pruebas %s %s >> %s 2>&1 &',
|
||||
escapeshellarg($phpPath),
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg($logPath)
|
||||
);
|
||||
|
||||
// Registrar el comando que se va a ejecutar para depuración
|
||||
error_log("Ejecutando comando: " . $command . "\n", 3, $logFile);
|
||||
|
||||
// Ejecutar el comando
|
||||
shell_exec($command);
|
||||
|
||||
// Registrar que se inició el procesamiento
|
||||
error_log("Procesamiento en segundo plano iniciado para schedule_id: " . ($scheduleId ?? 'nuevo') . "\n", 3, $logFile);
|
||||
}
|
||||
|
||||
header("Location: ../scheduled_messages.php?success={$success_param}");
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
error_log("MessageHandler Error: " . $e->getMessage() . "\n", 3, $logFile);
|
||||
$error_url = ($submitAction === 'update') ? "../create_message.php?action=edit&schedule_id={$scheduleId}&error=dberror" : '../create_message.php?error=dberror';
|
||||
header("Location: {$error_url}");
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
48
includes/message_handler_edit.php
Executable file
48
includes/message_handler_edit.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../scheduled_messages.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Basic validation
|
||||
if (empty($_POST['messageContent']) || empty($_POST['recipientId']) || empty($_POST['schedule_id']) || empty($_POST['message_id'])) {
|
||||
header('Location: ../edit_message.php?schedule_id=' . $_POST['schedule_id'] . '&error=missing_fields');
|
||||
exit();
|
||||
}
|
||||
|
||||
$scheduleId = $_POST['schedule_id'];
|
||||
$messageId = $_POST['message_id'];
|
||||
$content = $_POST['messageContent'];
|
||||
$recipientId = $_POST['recipientId'];
|
||||
$scheduleDateTime = $_POST['scheduleDateTime'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// 1. Update the message content
|
||||
$stmt = $pdo->prepare("UPDATE messages SET content = ? WHERE id = ?");
|
||||
$stmt->execute([$content, $messageId]);
|
||||
|
||||
// 2. Update the schedule
|
||||
// When editing, we reset its status to 'pending' so the worker can pick it up again.
|
||||
$stmt = $pdo->prepare(
|
||||
"UPDATE schedules SET recipient_id = ?, send_time = ?, status = 'pending' WHERE id = ?"
|
||||
);
|
||||
$stmt->execute([$recipientId, $scheduleDateTime, $scheduleId]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// Redirect to a success page
|
||||
header('Location: ../scheduled_messages.php?success=updated');
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
error_log("Message update failed: " . $e->getMessage());
|
||||
header('Location: ../edit_message.php?schedule_id=' . $scheduleId . '&error=dberror');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
97
includes/recurrent_message_handler.php
Executable file
97
includes/recurrent_message_handler.php
Executable file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/activity_logger.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../recurrentes.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
$username = $_SESSION['username'] ?? 'Unknown';
|
||||
|
||||
// Function to redirect with a status message
|
||||
function redirect_with_status($status, $message) {
|
||||
header("Location: ../recurrentes.php?status=$status&message=" . urlencode($message));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
$name = trim($_POST['messageName'] ?? '');
|
||||
$content = trim($_POST['messageContent'] ?? '');
|
||||
$telegram_command = trim($_POST['telegram_command'] ?? '');
|
||||
|
||||
if (empty($name) || empty($content)) {
|
||||
redirect_with_status('error', 'El nombre y el contenido del mensaje son obligatorios.');
|
||||
}
|
||||
|
||||
// Si el comando no está vacío, asegúrate de que no contenga el '#'
|
||||
if (!empty($telegram_command)) {
|
||||
$telegram_command = ltrim($telegram_command, '#');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO recurrent_messages (name, telegram_command, message_content)
|
||||
VALUES (?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$name, $telegram_command, $content]);
|
||||
log_activity($userId, 'Recurrent Message Created', 'User ' . $username . ' created recurrent message: ' . $name);
|
||||
redirect_with_status('success', 'Mensaje guardado exitosamente.');
|
||||
break;
|
||||
|
||||
case 'activate':
|
||||
case 'deactivate':
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$is_active = ($action === 'activate') ? 1 : 0;
|
||||
$stmt = $pdo->prepare("UPDATE recurrent_messages SET is_active = ? WHERE id = ?");
|
||||
$stmt->execute([$is_active, $id]);
|
||||
$status_message = ($action === 'activate') ? 'activado' : 'desactivado';
|
||||
log_activity($userId, 'Recurrent Message Status Change', 'User ' . $username . ' ' . $status_message . ' recurrent message ID: ' . $id);
|
||||
redirect_with_status('success', "Mensaje recurrente $status_message.");
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$name = trim($_POST['messageName'] ?? '');
|
||||
$content = trim($_POST['messageContent'] ?? '');
|
||||
$telegram_command = trim($_POST['telegram_command'] ?? '');
|
||||
|
||||
if (empty($id) || empty($name) || empty($content)) {
|
||||
redirect_with_status('error', 'ID, nombre y contenido del mensaje son obligatorios para actualizar.');
|
||||
}
|
||||
|
||||
// Si el comando no está vacío, asegúrate de que no contenga el '#'
|
||||
if (!empty($telegram_command)) {
|
||||
$telegram_command = ltrim($telegram_command, '#');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"UPDATE recurrent_messages SET name = ?, telegram_command = ?, message_content = ? WHERE id = ?"
|
||||
);
|
||||
$stmt->execute([$name, $telegram_command, $content, $id]);
|
||||
log_activity($userId, 'Recurrent Message Updated', 'User ' . $username . ' updated recurrent message ID: ' . $id . ' with name: ' . $name);
|
||||
redirect_with_status('success', 'Mensaje recurrente actualizado exitosamente.');
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$stmt = $pdo->prepare("DELETE FROM recurrent_messages WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
log_activity($userId, 'Recurrent Message Deleted', 'User ' . $username . ' deleted recurrent message ID: ' . $id);
|
||||
redirect_with_status('success', 'Mensaje recurrente eliminado.');
|
||||
break;
|
||||
|
||||
default:
|
||||
redirect_with_status('error', 'Acción no válida.');
|
||||
break;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Log the error in a real application
|
||||
error_log("Error in recurrent_message_handler: " . $e->getMessage());
|
||||
redirect_with_status('error', 'Error en la base de datos: ' . $e->getMessage());
|
||||
}
|
||||
70
includes/schedule_actions.php
Executable file
70
includes/schedule_actions.php
Executable file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/activity_logger.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../scheduled_messages.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$scheduleId = $_POST['schedule_id'] ?? null;
|
||||
$action = $_POST['action'] ?? null;
|
||||
$userId = $_SESSION['user_id'];
|
||||
|
||||
if (!$scheduleId || !$action) {
|
||||
header('Location: ../scheduled_messages.php?error=missing_data');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$newStatus = '';
|
||||
$successMessage = '';
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
$newStatus = 'disabled';
|
||||
$successMessage = 'disabled';
|
||||
log_activity($userId, 'Message Disabled', 'Schedule ID: ' . $scheduleId);
|
||||
break;
|
||||
case 'enable':
|
||||
$newStatus = 'pending';
|
||||
$successMessage = 'enabled';
|
||||
log_activity($userId, 'Message Enabled', 'Schedule ID: ' . $scheduleId);
|
||||
break;
|
||||
case 'cancel':
|
||||
$newStatus = 'cancelled';
|
||||
$successMessage = 'cancelled';
|
||||
log_activity($userId, 'Message Sending Cancelled', 'Schedule ID: ' . $scheduleId);
|
||||
break;
|
||||
case 'retry':
|
||||
$newStatus = 'pending';
|
||||
$successMessage = 'retried';
|
||||
log_activity($userId, 'Message Retried', 'Schedule ID: ' . $scheduleId);
|
||||
break;
|
||||
case 'delete':
|
||||
$stmt = $pdo->prepare("DELETE FROM schedules WHERE id = ?");
|
||||
$stmt->execute([$scheduleId]);
|
||||
$successMessage = 'deleted';
|
||||
log_activity($userId, 'Message Deleted', 'Schedule ID: ' . $scheduleId);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid action specified.');
|
||||
}
|
||||
|
||||
if ($newStatus) {
|
||||
$stmt = $pdo->prepare("UPDATE schedules SET status = ? WHERE id = ?");
|
||||
$stmt->execute([$newStatus, $scheduleId]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
header("Location: ../scheduled_messages.php?success={$successMessage}");
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
error_log("ScheduleActions Error: " . $e->getMessage());
|
||||
header('Location: ../scheduled_messages.php?error=dberror');
|
||||
exit();
|
||||
}
|
||||
16
includes/schedule_helpers.php
Executable file
16
includes/schedule_helpers.php
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* ARCHIVO BRIDGE - Migración Progresiva
|
||||
*
|
||||
* Este archivo mantiene compatibilidad con código existente
|
||||
* mientras redirige a la nueva ubicación del helper.
|
||||
*
|
||||
* Nueva ubicación: /common/helpers/schedule_helpers.php
|
||||
* Fecha de migración: 2025-11-25
|
||||
*/
|
||||
|
||||
// Cargar el archivo desde la nueva ubicación
|
||||
require_once __DIR__ . '/../common/helpers/schedule_helpers.php';
|
||||
|
||||
// Este archivo puede ser eliminado cuando toda la migración esté completa
|
||||
// y todas las referencias apunten directamente a common/helpers/schedule_helpers.php
|
||||
136
includes/scheduled_messages_table_body.php
Executable file
136
includes/scheduled_messages_table_body.php
Executable file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
// Este archivo contiene solo la lógica para renderizar el cuerpo de la tabla de mensajes programados.
|
||||
// Se espera que sea incluido por scheduled_messages.php o llamado vía AJAX.
|
||||
|
||||
// Asegurarse de que las dependencias necesarias estén cargadas si se llama directamente vía AJAX
|
||||
if (!defined('SCHEDULED_MESSAGES_LOADED')) {
|
||||
require_once __DIR__ . '/db.php';
|
||||
// La función getDayNames se define aquí para auto-contención
|
||||
function getDayNames($daysString) {
|
||||
if (empty($daysString)) return '<span data-translate="true">No especificado</span>';
|
||||
$dayMap = [
|
||||
0 => '<span data-translate="true">Domingo</span>',
|
||||
1 => '<span data-translate="true">Lunes</span>',
|
||||
2 => '<span data-translate="true">Martes</span>',
|
||||
3 => '<span data-translate="true">Miércoles</span>',
|
||||
4 => '<span data-translate="true">Jueves</span>',
|
||||
5 => '<span data-translate="true">Viernes</span>',
|
||||
6 => '<span data-translate="true">Sábado</span>'
|
||||
];
|
||||
$days = explode(',', $daysString);
|
||||
$names = array_map(fn($day) => $dayMap[(int)$day] ?? '', $days);
|
||||
return implode(', ', array_filter($names));
|
||||
}
|
||||
|
||||
// Si no se ha cargado $messages, cargarla aquí (para llamadas AJAX directas)
|
||||
if (!isset($messages)) {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT
|
||||
s.id as schedule_id,
|
||||
s.send_time,
|
||||
s.status,
|
||||
s.is_recurring,
|
||||
s.recurring_days,
|
||||
s.recurring_time,
|
||||
m.id as message_id,
|
||||
m.content,
|
||||
r.name as recipient_name,
|
||||
r.type as recipient_type,
|
||||
r.platform,
|
||||
u.username as creator_username
|
||||
FROM schedules s
|
||||
JOIN messages m ON s.message_id = m.id
|
||||
LEFT JOIN recipients r ON s.recipient_id = r.id
|
||||
JOIN users u ON m.user_id = u.id
|
||||
WHERE s.status IN ('draft', 'pending', 'failed', 'processing', 'disabled')
|
||||
ORDER BY s.created_at DESC"
|
||||
);
|
||||
$stmt->execute();
|
||||
$messages = $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if (empty($messages)):
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted" data-translate="true">No hay mensajes aquí.</td>
|
||||
</tr>
|
||||
<?php else:
|
||||
?>
|
||||
<?php foreach ($messages as $msg):
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if (!empty($msg['platform'])): ?>
|
||||
<span class="badge <?= $msg['platform'] === 'discord' ? 'bg-primary' : 'bg-info text-dark' ?>">
|
||||
<i class="bi bi-<?= $msg['platform'] === 'discord' ? 'discord' : 'telegram' ?>"></i>
|
||||
<?= htmlspecialchars(ucfirst($msg['platform'])) ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary" data-translate="true">N/A</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= !empty($msg['recipient_name']) ? htmlspecialchars($msg['recipient_name']) . ' <span class="text-muted" data-translate="true">(' . $msg['recipient_type'] . ')</span>' : '<span class="text-muted" data-translate="true">No asignado</span>' ?></td>
|
||||
<td>
|
||||
<div class="message-preview">
|
||||
<?= substr(strip_tags($msg['content']), 0, 100); ?>...
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($msg['is_recurring'] == 1):
|
||||
echo '<span data-translate="true">Semanal:</span> ' . getDayNames($msg['recurring_days']) . ' <span data-translate="true">a las</span> ' . substr($msg['recurring_time'], 0, 5);
|
||||
else:
|
||||
echo $msg['send_time'] ? date('d/m/Y H:i', strtotime($msg['send_time'])) : '-';
|
||||
endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$statusBadges = [
|
||||
'pending' => 'info text-dark', 'draft' => 'secondary', 'failed' => 'danger',
|
||||
'processing' => 'warning text-dark', 'disabled' => 'light text-dark'
|
||||
];
|
||||
$badgeClass = $statusBadges[$msg['status']] ?? 'light';
|
||||
?>
|
||||
<span class="badge bg-<?= $badgeClass ?>"><?= ucfirst($msg['status']) ?></span>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($msg['creator_username']) ?></td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<a href="preview_message.php?id=<?= $msg['message_id'] ?>" target="_blank" class="btn btn-sm btn-info" title="Previsualizar" data-translate-title="true">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a href="create_message.php?schedule_id=<?= $msg['schedule_id'] ?>&action=edit" class="btn btn-sm btn-primary" title="Editar" data-translate-title="true">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</a>
|
||||
<form action="includes/schedule_actions.php" method="POST" class="d-inline">
|
||||
<input type="hidden" name="schedule_id" value="<?= $msg['schedule_id'] ?>">
|
||||
<input type="hidden" name="message_id" value="<?= $msg['message_id'] ?>">
|
||||
|
||||
<?php if (in_array($msg['status'], ['pending', 'disabled'])): ?>
|
||||
<button type="submit" name="action" value="<?= $msg['status'] === 'pending' ? 'disable' : 'enable' ?>" class="btn btn-sm btn-secondary" data-translate-title="true" title="<?= $msg['status'] === 'pending' ? 'Deshabilitar' : 'Habilitar' ?>">
|
||||
<i class="bi <?= $msg['status'] === 'pending' ? 'bi-pause-circle' : 'bi-play-circle' ?>"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($msg['status'] === 'failed'): ?>
|
||||
<button type="submit" name="action" value="retry" class="btn btn-sm btn-warning text-dark" title="Reintentar Envío" data-translate-title="true">
|
||||
<i class="bi bi-arrow-repeat"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($msg['status'] === 'processing'): ?>
|
||||
<button type="submit" name="action" value="cancel" class="btn btn-sm btn-warning" title="Cancelar Envío" data-translate-title="true" data-confirm-message="¿Estás seguro de que quieres cancelar este envío?" data-translate-confirm="true" onclick="return confirm(this.getAttribute('data-confirm-message'));">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="submit" name="action" value="delete" class="btn btn-sm btn-danger" title="Borrar" data-translate-title="true" data-confirm-message="¿Estás seguro de que quieres borrar este mensaje?" data-translate-confirm="true" onclick="return confirm(this.getAttribute('data-confirm-message'));">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
25
includes/session_check.php
Executable file
25
includes/session_check.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
ini_set('session.cookie_secure', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
// Must be the very first thing on any protected page
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Incluir el helper de URLs y establecer las cabeceras de seguridad
|
||||
require_once __DIR__ . '/../includes/url_helper.php';
|
||||
require_once __DIR__ . '/db.php'; // Asegura que $pdo esté disponible
|
||||
require_once __DIR__ . '/activity_logger.php'; // Asegura que log_activity esté disponible
|
||||
set_security_headers();
|
||||
|
||||
// Generar token CSRF si no existe
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
// Verificar si el usuario ha iniciado sesión
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: ' . site_url('login.php'));
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
75
includes/telegram_actions.php
Executable file
75
includes/telegram_actions.php
Executable file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
require_once __DIR__ . '/../src/TelegramSender.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['action'])) {
|
||||
header('Location: ../sent_messages.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$action = $_POST['action'];
|
||||
|
||||
if ($action === 'delete_message') {
|
||||
if (!isset($_POST['sent_message_id'], $_POST['platform_message_id'], $_POST['chat_id'])) {
|
||||
header('Location: ../sent_messages.php?error=missing_data');
|
||||
exit();
|
||||
}
|
||||
|
||||
$sentMessageId = $_POST['sent_message_id'];
|
||||
$telegramMessageIdsJson = $_POST['platform_message_id'];
|
||||
$chatId = $_POST['chat_id'];
|
||||
|
||||
$telegramMessageIds = json_decode($telegramMessageIdsJson, true);
|
||||
|
||||
// If decoding fails or the result is not an array, treat it as a single ID
|
||||
if (json_last_error() !== JSON_ERROR_NONE || !is_array($telegramMessageIds)) {
|
||||
$telegramMessageIds = [$telegramMessageIdsJson];
|
||||
}
|
||||
|
||||
$telegramSender = new TelegramSender(TELEGRAM_BOT_TOKEN);
|
||||
$allDeleted = true;
|
||||
$error_messages = [];
|
||||
|
||||
try {
|
||||
foreach ($telegramMessageIds as $messageId) {
|
||||
// Skip if the ID is empty or invalid
|
||||
if (empty($messageId) || !is_numeric($messageId)) {
|
||||
error_log("Skipping invalid Telegram message ID: " . var_export($messageId, true));
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$telegramSender->deleteMessage($chatId, $messageId);
|
||||
usleep(300000); // 300ms pause to avoid rate limits
|
||||
} catch (Exception $e) {
|
||||
$allDeleted = false;
|
||||
$error_messages[] = "Failed to delete message ID {$messageId}: " . $e->getMessage();
|
||||
error_log(end($error_messages)); // Log the last error
|
||||
}
|
||||
}
|
||||
|
||||
if ($allDeleted) {
|
||||
// If all messages were deleted successfully, remove the record from DB
|
||||
$stmt = $pdo->prepare("DELETE FROM sent_messages WHERE id = ?");
|
||||
$stmt->execute([$sentMessageId]);
|
||||
header('Location: ../sent_messages.php?success=deleted&platform=Telegram');
|
||||
} else {
|
||||
// If some deletions failed, redirect with an error message
|
||||
$errorMessage = implode('; ', $error_messages);
|
||||
header('Location: ../sent_messages.php?error=delete_failed&platform=Telegram&message=' . urlencode($errorMessage));
|
||||
}
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Catch any other unexpected errors during the process
|
||||
$errorMessage = "An unexpected error occurred: " . $e->getMessage();
|
||||
error_log($errorMessage);
|
||||
header('Location: ../sent_messages.php?error=unexpected_error&platform=Telegram&message=' . urlencode($errorMessage));
|
||||
exit();
|
||||
}
|
||||
} // Closing brace for "if ($action === 'delete_message')"
|
||||
|
||||
// Fallback redirect if action is not 'delete_message'
|
||||
header('Location: ../sent_messages.php');
|
||||
exit();
|
||||
210
includes/translation_helper.php
Executable file
210
includes/translation_helper.php
Executable file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* Helper para manejar traducciones en la aplicación
|
||||
*
|
||||
* Este archivo proporciona funciones para manejar traducciones de manera consistente
|
||||
* en toda la aplicación, incluyendo soporte para múltiples idiomas y caché de traducciones.
|
||||
*/
|
||||
|
||||
// Evitar acceso directo
|
||||
defined('ROOT_PATH') || define('ROOT_PATH', dirname(__DIR__));
|
||||
|
||||
// Inicializar el array de caché de traducciones
|
||||
$GLOBALS['_translations_cache'] = [];
|
||||
|
||||
/**
|
||||
* Obtiene una traducción para la clave dada en el idioma actual del usuario
|
||||
*
|
||||
* @param string $key Clave de traducción
|
||||
* @param array $params Parámetros para reemplazar en la cadena de traducción
|
||||
* @param string|null $language Código de idioma (opcional, por defecto usa el idioma de sesión)
|
||||
* @return string Texto traducido o la clave si no se encuentra la traducción
|
||||
*/
|
||||
function __($key, $params = [], $language = null) {
|
||||
global $pdo; // Asumiendo que $pdo está disponible globalmente
|
||||
|
||||
// Si no se proporciona un idioma, usar el de la sesión o el predeterminado
|
||||
if ($language === null) {
|
||||
$language = $_SESSION['language'] ?? 'es';
|
||||
}
|
||||
|
||||
// Clave para el caché
|
||||
$cache_key = $language . '_' . $key;
|
||||
|
||||
// Verificar si la traducción está en caché
|
||||
if (isset($GLOBALS['_translations_cache'][$cache_key])) {
|
||||
$translation = $GLOBALS['_translations_cache'][$cache_key];
|
||||
} else {
|
||||
// Si no está en caché, buscarla en la base de datos
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT `value` FROM translations
|
||||
WHERE `key` = :key AND language_code = :language
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$stmt->execute([
|
||||
':key' => $key,
|
||||
':language' => $language
|
||||
]);
|
||||
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$translation = $result ? $result['value'] : '';
|
||||
|
||||
// Almacenar en caché para futuras solicitudes
|
||||
$GLOBALS['_translations_cache'][$cache_key] = $translation;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// En caso de error, devolver la clave como último recurso
|
||||
error_log("Error al obtener traducción: " . $e->getMessage());
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
// Si no se encontró la traducción, devolver la clave
|
||||
if (empty($translation)) {
|
||||
// Opcional: Registrar claves faltantes para facilitar la localización
|
||||
log_missing_translation($key, $language);
|
||||
return $key;
|
||||
}
|
||||
|
||||
// Reemplazar parámetros si se proporcionan
|
||||
if (!empty($params) && is_array($params)) {
|
||||
foreach ($params as $param => $value) {
|
||||
$translation = str_replace(":$param", $value, $translation);
|
||||
}
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra claves de traducción faltantes para facilitar la localización
|
||||
*
|
||||
* @param string $key Clave de traducción faltante
|
||||
* @param string $language Código de idioma
|
||||
*/
|
||||
function log_missing_translation($key, $language) {
|
||||
$log_file = ROOT_PATH . '/logs/missing_translations.log';
|
||||
$log_entry = sprintf(
|
||||
"[%s] Missing translation: %s (Language: %s)\n",
|
||||
date('Y-m-d H:i:s'),
|
||||
$key,
|
||||
$language
|
||||
);
|
||||
|
||||
// Asegurarse de que el directorio de logs existe
|
||||
if (!is_dir(dirname($log_file))) {
|
||||
@mkdir(dirname($log_file), 0755, true);
|
||||
}
|
||||
|
||||
// Registrar en el archivo de log
|
||||
@file_put_contents($log_file, $log_entry, FILE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene la lista de idiomas disponibles
|
||||
*
|
||||
* @param bool $only_active Si es true, solo devuelve los idiomas activos
|
||||
* @return array Lista de idiomas
|
||||
*/
|
||||
function get_available_languages($only_active = true) {
|
||||
global $pdo;
|
||||
|
||||
try {
|
||||
$sql = "SELECT code, name, native_name, flag_emoji, is_active
|
||||
FROM languages";
|
||||
|
||||
if ($only_active) {
|
||||
$sql .= " WHERE is_active = 1";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY name";
|
||||
|
||||
$stmt = $pdo->query($sql);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error al obtener idiomas: " . $e->getMessage());
|
||||
return [
|
||||
['code' => 'es', 'name' => 'Spanish', 'native_name' => 'Español', 'flag_emoji' => '🇪🇸', 'is_active' => 1],
|
||||
['code' => 'en', 'name' => 'English', 'native_name' => 'English', 'flag_emoji' => '🇬🇧', 'is_active' => 1]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa el sistema de traducciones en las plantillas
|
||||
* Inyecta las traducciones necesarias para JavaScript
|
||||
*
|
||||
* @return string Código JavaScript con las traducciones (sin etiquetas <script>)
|
||||
*/
|
||||
function init_translations_for_js() {
|
||||
global $pdo;
|
||||
|
||||
// Obtener el idioma actual
|
||||
$language = $_SESSION['language'] ?? 'es';
|
||||
|
||||
// Obtener todas las traducciones para el idioma actual
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT `key`, `value` FROM translations
|
||||
WHERE language_code = :language"
|
||||
);
|
||||
|
||||
$stmt->execute([':language' => $language]);
|
||||
$translations = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error al cargar traducciones para JS: " . $e->getMessage());
|
||||
$translations = [];
|
||||
}
|
||||
|
||||
// Devolver solo el código JavaScript (sin etiquetas <script>)
|
||||
return "// Traducciones cargadas dinámicamente\n" .
|
||||
"window.translations = " . json_encode($translations, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE) . ";\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Función para traducir texto en JavaScript
|
||||
* Se debe llamar después de incluir el helper en el HTML
|
||||
*/
|
||||
function js_translation_function() {
|
||||
echo <<<JS
|
||||
<script>
|
||||
/**
|
||||
* Función para traducir texto en JavaScript
|
||||
* @param {string} key - Clave de traducción
|
||||
* @param {Object} params - Parámetros para reemplazar en la cadena
|
||||
* @return {string} Texto traducido o la clave si no se encuentra
|
||||
*/
|
||||
function __(key, params = {}) {
|
||||
let text = translations[key] || key;
|
||||
|
||||
// Reemplazar parámetros
|
||||
Object.keys(params).forEach(param => {
|
||||
text = text.replace(new RegExp(`:${param}`, 'g'), params[param]);
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// Traducir elementos con el atributo data-translate
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const elements = document.querySelectorAll('[data-translate]');
|
||||
elements.forEach(element => {
|
||||
const key = element.getAttribute('data-translate');
|
||||
if (translations[key]) {
|
||||
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
||||
element.placeholder = translations[key];
|
||||
} else if (element.tagName === 'BUTTON' || element.tagName === 'A') {
|
||||
element.textContent = translations[key];
|
||||
} else {
|
||||
element.textContent = translations[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
JS;
|
||||
}
|
||||
79
includes/tren_handler.php
Executable file
79
includes/tren_handler.php
Executable file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/session_check.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../tren.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$content = $_POST['messageContent'] ?? '';
|
||||
$recipientId = $_POST['recipientId'] ?? '';
|
||||
$scheduleType = $_POST['scheduleType'] ?? 'now';
|
||||
|
||||
// Validation
|
||||
if (empty($recipientId) || empty($content)) {
|
||||
header('Location: ../tren.php?error=missing_fields');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// 1. Save the message content
|
||||
$stmt = $pdo->prepare("INSERT INTO messages (user_id, content) VALUES (?, ?)");
|
||||
$stmt->execute([$userId, $content]);
|
||||
$messageId = $pdo->lastInsertId();
|
||||
|
||||
// 2. Schedule the message
|
||||
$sendTime = null;
|
||||
$status = 'pending';
|
||||
|
||||
if ($scheduleType === 'later') {
|
||||
if (empty($_POST['scheduleDateTime'])) {
|
||||
throw new Exception('La fecha y hora de envío son requeridas para programar.');
|
||||
}
|
||||
$sendTime = (new DateTime($_POST['scheduleDateTime'], new DateTimeZone('America/Mexico_City')))->format('Y-m-d H:i:s');
|
||||
} else { // 'now'
|
||||
$sendTime = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO schedules (message_id, recipient_id, send_time, status, is_recurring, recurring_days, recurring_time) VALUES (?, ?, ?, ?, 0, NULL, NULL)"
|
||||
);
|
||||
$stmt->execute([$messageId, $recipientId, $sendTime, $status]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// If it's an immediate send, trigger processing
|
||||
if ($scheduleType === 'now') {
|
||||
$phpPath = PHP_BINARY ?: 'php';
|
||||
$scriptPath = dirname(__DIR__) . '/process_queue.php';
|
||||
$logPath = dirname(__DIR__) . '/logs/process_queue_manual.log';
|
||||
|
||||
// Asegurarse de que se use el entorno correcto
|
||||
$command = sprintf(
|
||||
'APP_ENVIRONMENT=pruebas %s %s >> %s 2>&1 &',
|
||||
escapeshellarg($phpPath),
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg($logPath)
|
||||
);
|
||||
|
||||
// Registrar el comando que se va a ejecutar para depuración
|
||||
error_log("Ejecutando comando desde tren_handler.php: " . $command . "\n", 3, dirname(__DIR__) . '/logs/tren_handler.log');
|
||||
|
||||
// Ejecutar el comando
|
||||
shell_exec($command);
|
||||
}
|
||||
|
||||
header('Location: ../scheduled_messages.php?success=message_created');
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
// Log the error properly in a real application
|
||||
header('Location: ../tren.php?error=dberror');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
16
includes/url_helper.php
Executable file
16
includes/url_helper.php
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* ARCHIVO BRIDGE - Migración Progresiva
|
||||
*
|
||||
* Este archivo mantiene compatibilidad con código existente
|
||||
* mientras redirige a la nueva ubicación del helper.
|
||||
*
|
||||
* Nueva ubicación: /common/helpers/url_helper.php
|
||||
* Fecha de migración: 2025-11-25
|
||||
*/
|
||||
|
||||
// Cargar el archivo desde la nueva ubicación
|
||||
require_once __DIR__ . '/../common/helpers/url_helper.php';
|
||||
|
||||
// Este archivo puede ser eliminado cuando toda la migración esté completa
|
||||
// y todas las referencias apunten directamente a common/helpers/url_helper.php
|
||||
Reference in New Issue
Block a user