Initial commit - Last War messaging system
This commit is contained in:
31
includes/activity_logger.php
Executable file
31
includes/activity_logger.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
function logActivity(int $userId, string $action, string $details): void
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$username = $_SESSION['username'] ?? 'system';
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO activity_log (user_id, username, action, details, timestamp) VALUES (?, ?, ?, ?, NOW())");
|
||||
$stmt->execute([$userId, $username, $action, $details]);
|
||||
}
|
||||
|
||||
function getActivityLog(?int $limit = 100): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM activity_log ORDER BY timestamp DESC LIMIT ?");
|
||||
$stmt->execute([$limit]);
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
function getUserActivity(int $userId, ?int $limit = 50): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM activity_log WHERE user_id = ? ORDER BY timestamp DESC LIMIT ?");
|
||||
$stmt->execute([$userId, $limit]);
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
87
includes/auth.php
Executable file
87
includes/auth.php
Executable file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/activity_logger.php';
|
||||
|
||||
function loginUser(string $username, string $password): ?array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user || !password_verify($password, $user['password'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
|
||||
logActivity($user['id'], 'login', 'Usuario inició sesión');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function registerUser(string $username, string $password, string $role = 'user'): ?int
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $hashedPassword, $role]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function updateUserPassword(int $userId, string $newPassword): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
return $stmt->execute([$hashedPassword, $userId]);
|
||||
}
|
||||
|
||||
function getUserById(int $userId): ?array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id, username, role, telegram_chat_id, created_at FROM users WHERE id = ?");
|
||||
$stmt->execute([$userId]);
|
||||
|
||||
return $stmt->fetch() ?: null;
|
||||
}
|
||||
|
||||
function getAllUsers(): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->query("SELECT id, username, role, telegram_chat_id, created_at FROM users ORDER BY username");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
function updateUserTelegramChatId(int $userId, string $telegramChatId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE users SET telegram_chat_id = ? WHERE id = ?");
|
||||
return $stmt->execute([$telegramChatId, $userId]);
|
||||
}
|
||||
|
||||
function deleteUser(int $userId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
return $stmt->execute([$userId]);
|
||||
}
|
||||
35
includes/db.php
Executable file
35
includes/db.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/env_loader.php';
|
||||
|
||||
function getDbConnection(): PDO
|
||||
{
|
||||
static $pdo = null;
|
||||
|
||||
if ($pdo === null) {
|
||||
$host = $_ENV['DB_HOST'] ?? getenv('DB_HOST') ?? 'localhost';
|
||||
$port = $_ENV['DB_PORT'] ?? getenv('DB_PORT') ?? '3306';
|
||||
$dbname = $_ENV['DB_NAME'] ?? getenv('DB_NAME') ?? 'bot';
|
||||
$user = $_ENV['DB_USER'] ?? getenv('DB_USER') ?? 'root';
|
||||
$pass = $_ENV['DB_PASS'] ?? getenv('DB_PASS') ?? '';
|
||||
|
||||
$dsn = "mysql:host={$host};port={$port};dbname={$dbname};charset=utf8mb4";
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
$pdo = new PDO($dsn, $user, $pass, $options);
|
||||
|
||||
// Configurar zona horaria
|
||||
$timezone = $_ENV['TZ'] ?? getenv('TZ') ?? 'America/Mexico_City';
|
||||
date_default_timezone_set($timezone);
|
||||
|
||||
// Configurar timezone de MySQL
|
||||
$pdo->exec("SET time_zone = '" . date('P') . "'");
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
32
includes/env_loader.php
Executable file
32
includes/env_loader.php
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
function loadEnv(string $path): void
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (strpos(trim($line), '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($line, '=') !== false) {
|
||||
list($key, $value) = explode('=', $line, 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
|
||||
if (!array_key_exists($key, $_ENV)) {
|
||||
$_ENV[$key] = $value;
|
||||
}
|
||||
|
||||
if (!getenv($key)) {
|
||||
putenv("$key=$value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadEnv(__DIR__ . '/../.env');
|
||||
41
includes/logger.php
Executable file
41
includes/logger.php
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
function custom_log(string $message, string $level = 'INFO'): void
|
||||
{
|
||||
$logDir = __DIR__ . '/../logs';
|
||||
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
$logFile = $logDir . '/app.log';
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'CLI';
|
||||
$user = $_SESSION['username'] ?? 'guest';
|
||||
|
||||
$logMessage = "[{$timestamp}] [{$level}] [{$ip}] [{$user}] {$message}" . PHP_EOL;
|
||||
|
||||
file_put_contents($logFile, $logMessage, FILE_APPEND);
|
||||
}
|
||||
|
||||
function logError(string $message): void
|
||||
{
|
||||
custom_log($message, 'ERROR');
|
||||
}
|
||||
|
||||
function logWarning(string $message): void
|
||||
{
|
||||
custom_log($message, 'WARNING');
|
||||
}
|
||||
|
||||
function logInfo(string $message): void
|
||||
{
|
||||
custom_log($message, 'INFO');
|
||||
}
|
||||
|
||||
function logDebug(string $message): void
|
||||
{
|
||||
if ($_ENV['APP_ENVIRONMENT'] ?? getenv('APP_ENVIRONMENT') === 'pruebas') {
|
||||
custom_log($message, 'DEBUG');
|
||||
}
|
||||
}
|
||||
229
includes/message_handler.php
Executable file
229
includes/message_handler.php
Executable file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/activity_logger.php';
|
||||
|
||||
function createMessage(array $data): int
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO messages (user_id, content, created_at)
|
||||
VALUES (?, ?, NOW())
|
||||
");
|
||||
$stmt->execute([$data['user_id'], $data['content']]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function createSchedule(array $data): int
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO schedules (
|
||||
message_id, recipient_id, send_time, status,
|
||||
is_recurring, recurring_days, recurring_time, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$data['message_id'],
|
||||
$data['recipient_id'],
|
||||
$data['send_time'],
|
||||
$data['status'] ?? 'pending',
|
||||
($data['is_recurring'] ?? false) ? 1 : 0,
|
||||
$data['recurring_days'] ?? null,
|
||||
$data['recurring_time'] ?? null
|
||||
]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function handleCreateMessage(array $postData): array
|
||||
{
|
||||
$required = ['content', 'recipient_id', 'send_type'];
|
||||
|
||||
foreach ($required as $field) {
|
||||
if (empty($postData[$field])) {
|
||||
return ['success' => false, 'error' => "Falta el campo: {$field}"];
|
||||
}
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
$messageId = createMessage([
|
||||
'user_id' => $userId,
|
||||
'content' => $postData['content']
|
||||
]);
|
||||
|
||||
$sendTime = match ($postData['send_type']) {
|
||||
'now' => date('Y-m-d H:i:s'),
|
||||
'later' => $postData['send_datetime'],
|
||||
'recurring' => calculateNextSendTime($postData['recurring_days'], $postData['recurring_time']),
|
||||
default => date('Y-m-d H:i:s')
|
||||
};
|
||||
|
||||
$scheduleId = createSchedule([
|
||||
'message_id' => $messageId,
|
||||
'recipient_id' => $postData['recipient_id'],
|
||||
'send_time' => $sendTime,
|
||||
'status' => $postData['send_type'] === 'now' ? 'pending' : 'pending',
|
||||
'is_recurring' => $postData['send_type'] === 'recurring',
|
||||
'recurring_days' => $postData['recurring_days'] ?? null,
|
||||
'recurring_time' => $postData['recurring_time'] ?? null
|
||||
]);
|
||||
|
||||
logActivity($userId, 'create_message', "Mensaje creado ID: {$messageId}, Programación ID: {$scheduleId}");
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message_id' => $messageId,
|
||||
'schedule_id' => $scheduleId
|
||||
];
|
||||
}
|
||||
|
||||
function updateMessage(int $messageId, string $content): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE messages SET content = ? WHERE id = ?");
|
||||
return $stmt->execute([$content, $messageId]);
|
||||
}
|
||||
|
||||
function updateSchedule(int $scheduleId, array $data): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$fields = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$fields[] = "{$key} = ?";
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$values[] = $scheduleId;
|
||||
|
||||
$sql = "UPDATE schedules SET " . implode(', ', $fields) . " WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
return $stmt->execute($values);
|
||||
}
|
||||
|
||||
function handleEditMessage(int $messageId, array $postData): array
|
||||
{
|
||||
if (empty($postData['content']) || empty($postData['recipient_id'])) {
|
||||
return ['success' => false, 'error' => 'Faltan campos requeridos'];
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
|
||||
updateMessage($messageId, $postData['content']);
|
||||
|
||||
if (!empty($postData['schedule_id'])) {
|
||||
$sendTime = match ($postData['send_type']) {
|
||||
'now' => date('Y-m-d H:i:s'),
|
||||
'later' => $postData['send_datetime'],
|
||||
'recurring' => calculateNextSendTime($postData['recurring_days'] ?? '', $postData['recurring_time'] ?? ''),
|
||||
default => $postData['send_datetime']
|
||||
};
|
||||
|
||||
updateSchedule((int) $postData['schedule_id'], [
|
||||
'recipient_id' => $postData['recipient_id'],
|
||||
'send_time' => $sendTime,
|
||||
'is_recurring' => $postData['send_type'] === 'recurring',
|
||||
'recurring_days' => $postData['recurring_days'] ?? null,
|
||||
'recurring_time' => $postData['recurring_time'] ?? null,
|
||||
'status' => 'pending'
|
||||
]);
|
||||
}
|
||||
|
||||
logActivity($userId, 'edit_message', "Mensaje actualizado ID: {$messageId}");
|
||||
|
||||
return ['success' => true, 'message_id' => $messageId];
|
||||
}
|
||||
|
||||
function deleteMessage(int $messageId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM schedules WHERE message_id = ?");
|
||||
$stmt->execute([$messageId]);
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM messages WHERE id = ?");
|
||||
$result = $stmt->execute([$messageId]);
|
||||
|
||||
if ($result) {
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'delete_message', "Mensaje eliminado ID: {$messageId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getMessageById(int $messageId): ?array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM messages WHERE id = ?");
|
||||
$stmt->execute([$messageId]);
|
||||
|
||||
return $stmt->fetch() ?: null;
|
||||
}
|
||||
|
||||
function getScheduleById(int $scheduleId): ?array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM schedules WHERE id = ?");
|
||||
$stmt->execute([$scheduleId]);
|
||||
|
||||
return $stmt->fetch() ?: null;
|
||||
}
|
||||
|
||||
function getScheduledMessages(?int $userId = null): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$sql = "
|
||||
SELECT s.*, m.content, r.name as recipient_name, r.platform, r.platform_id
|
||||
FROM schedules s
|
||||
JOIN messages m ON s.message_id = m.id
|
||||
JOIN recipients r ON s.recipient_id = r.id
|
||||
";
|
||||
|
||||
if ($userId) {
|
||||
$sql .= " WHERE m.user_id = ?";
|
||||
$sql .= " ORDER BY s.send_time ASC";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$userId]);
|
||||
} else {
|
||||
$sql .= " ORDER BY s.send_time ASC";
|
||||
$stmt = $pdo->query($sql);
|
||||
}
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
function getSentMessages(?int $userId = null, ?int $limit = 50): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$sql = "
|
||||
SELECT sm.*, r.name as recipient_name, r.platform, r.platform_id
|
||||
FROM sent_messages sm
|
||||
JOIN recipients r ON sm.recipient_id = r.id
|
||||
";
|
||||
|
||||
if ($userId) {
|
||||
$sql .= " WHERE sm.user_id = ?";
|
||||
$sql .= " ORDER BY sm.sent_at DESC LIMIT ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$userId, $limit]);
|
||||
} else {
|
||||
$sql .= " ORDER BY sm.sent_at DESC LIMIT ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$limit]);
|
||||
}
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
98
includes/schedule_actions.php
Executable file
98
includes/schedule_actions.php
Executable file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
function disableSchedule(int $scheduleId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE schedules SET status = 'disabled' WHERE id = ?");
|
||||
$result = $stmt->execute([$scheduleId]);
|
||||
|
||||
if ($result) {
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'disable_schedule', "Programación deshabilitada ID: {$scheduleId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function enableSchedule(int $scheduleId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE schedules SET status = 'pending' WHERE id = ? AND status = 'disabled'");
|
||||
$result = $stmt->execute([$scheduleId]);
|
||||
|
||||
if ($result) {
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'enable_schedule', "Programación habilitada ID: {$scheduleId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function cancelSchedule(int $scheduleId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE schedules SET status = 'cancelled' WHERE id = ?");
|
||||
$result = $stmt->execute([$scheduleId]);
|
||||
|
||||
if ($result) {
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'cancel_schedule', "Programación cancelada ID: {$scheduleId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function retrySchedule(int $scheduleId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE schedules SET status = 'pending', error_message = NULL WHERE id = ? AND status = 'failed'");
|
||||
$result = $stmt->execute([$scheduleId]);
|
||||
|
||||
if ($result) {
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'retry_schedule', "Programación reintentada ID: {$scheduleId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function deleteSchedule(int $scheduleId): bool
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT message_id FROM schedules WHERE id = ?");
|
||||
$stmt->execute([$scheduleId]);
|
||||
$schedule = $stmt->fetch();
|
||||
|
||||
if ($schedule) {
|
||||
$stmt = $pdo->prepare("DELETE FROM schedules WHERE id = ?");
|
||||
$stmt->execute([$scheduleId]);
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM messages WHERE id = ?");
|
||||
$stmt->execute([$schedule['message_id']]);
|
||||
|
||||
logActivity($_SESSION['user_id'] ?? 0, 'delete_schedule', "Programación eliminada ID: {$scheduleId}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleScheduleAction(int $scheduleId, string $action): array
|
||||
{
|
||||
$result = match ($action) {
|
||||
'disable' => disableSchedule($scheduleId),
|
||||
'enable' => enableSchedule($scheduleId),
|
||||
'cancel' => cancelSchedule($scheduleId),
|
||||
'retry' => retrySchedule($scheduleId),
|
||||
'delete' => deleteSchedule($scheduleId),
|
||||
default => false
|
||||
};
|
||||
|
||||
return [
|
||||
'success' => $result,
|
||||
'action' => $action,
|
||||
'schedule_id' => $scheduleId
|
||||
];
|
||||
}
|
||||
98
includes/schedule_helpers.php
Executable file
98
includes/schedule_helpers.php
Executable file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
function calculateNextSendTime(string $recurringDays, string $recurringTime): ?string
|
||||
{
|
||||
$daysMap = [
|
||||
'sunday' => 0,
|
||||
'monday' => 1,
|
||||
'tuesday' => 2,
|
||||
'wednesday' => 3,
|
||||
'thursday' => 4,
|
||||
'friday' => 5,
|
||||
'saturday' => 6
|
||||
];
|
||||
|
||||
$days = array_map('trim', explode(',', strtolower($recurringDays)));
|
||||
$days = array_filter($days, fn($d) => isset($daysMap[$d]));
|
||||
$days = array_map(fn($d) => $daysMap[$d], $days);
|
||||
|
||||
if (empty($days)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$timeParts = explode(':', $recurringTime);
|
||||
$hour = (int) ($timeParts[0] ?? 0);
|
||||
$minute = (int) ($timeParts[1] ?? 0);
|
||||
|
||||
$now = new DateTime('now', new DateTimeZone('America/Mexico_City'));
|
||||
$currentDay = (int) $now->format('w');
|
||||
$currentHour = (int) $now->format('H');
|
||||
$currentMinute = (int) $now->format('i');
|
||||
|
||||
sort($days);
|
||||
|
||||
foreach ($days as $day) {
|
||||
if ($day > $currentDay || ($day === $currentDay && ($hour > $currentHour || ($hour === $currentHour && $minute > $currentMinute)))) {
|
||||
$next = clone $now;
|
||||
$next->setTime($hour, $minute);
|
||||
$next->modify('+' . ($day - $currentDay) . ' days');
|
||||
return $next->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
$daysAhead = (7 - $currentDay) + $days[0];
|
||||
$next = clone $now;
|
||||
$next->setTime($hour, $minute);
|
||||
$next->modify('+' . $daysAhead . ' days');
|
||||
|
||||
return $next->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
function getRecurringDaysOptions(): array
|
||||
{
|
||||
return [
|
||||
['value' => 'monday', 'label' => 'Lunes'],
|
||||
['value' => 'tuesday', 'label' => 'Martes'],
|
||||
['value' => 'wednesday', 'label' => 'Miércoles'],
|
||||
['value' => 'thursday', 'label' => 'Jueves'],
|
||||
['value' => 'friday', 'label' => 'Viernes'],
|
||||
['value' => 'saturday', 'label' => 'Sábado'],
|
||||
['value' => 'sunday', 'label' => 'Domingo']
|
||||
];
|
||||
}
|
||||
|
||||
function formatRecurringDays(string $days): string
|
||||
{
|
||||
$daysMap = [
|
||||
'monday' => 'Lun',
|
||||
'tuesday' => 'Mar',
|
||||
'wednesday' => 'Mié',
|
||||
'thursday' => 'Jue',
|
||||
'friday' => 'Vie',
|
||||
'saturday' => 'Sáb',
|
||||
'sunday' => 'Dom'
|
||||
];
|
||||
|
||||
$daysArray = array_map('trim', explode(',', strtolower($days)));
|
||||
|
||||
$result = [];
|
||||
foreach ($daysArray as $day) {
|
||||
$result[] = $daysMap[$day] ?? $day;
|
||||
}
|
||||
|
||||
return implode(', ', $result);
|
||||
}
|
||||
|
||||
function isValidRecurringDays(string $days): bool
|
||||
{
|
||||
$validDays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||||
$daysArray = array_map('trim', explode(',', strtolower($days)));
|
||||
|
||||
foreach ($daysArray as $day) {
|
||||
if (!in_array($day, $validDays)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($daysArray);
|
||||
}
|
||||
87
includes/session_check.php
Executable file
87
includes/session_check.php
Executable file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/env_loader.php';
|
||||
|
||||
function checkSession(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
$domain = $_ENV['APP_URL'] ?? getenv('APP_URL') ?? '';
|
||||
if ($domain) {
|
||||
$parsed = parse_url($domain);
|
||||
$host = $parsed['host'] ?? $_SERVER['HTTP_HOST'] ?? '';
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => $host,
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
|
||||
validateSessionDomain();
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
$basePath = dirname($_SERVER['PHP_SELF']);
|
||||
if ($basePath === '/' || $basePath === '\\') {
|
||||
$basePath = '';
|
||||
}
|
||||
header('Location: ' . $basePath . '/login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function validateSessionDomain(): void
|
||||
{
|
||||
$allowedDomain = $_ENV['APP_URL'] ?? getenv('APP_URL') ?? '';
|
||||
|
||||
if (empty($allowedDomain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parsed = parse_url($allowedDomain);
|
||||
$allowedHost = $parsed['host'] ?? '';
|
||||
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
|
||||
|
||||
if (strcasecmp($allowedHost, $currentHost) !== 0) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
$scheme = $parsed['scheme'] ?? 'https';
|
||||
$loginUrl = $scheme . '://' . $allowedHost . '/login.php';
|
||||
header('Location: ' . $loginUrl);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function isAdmin(): bool
|
||||
{
|
||||
return isset($_SESSION['role']) && $_SESSION['role'] === 'admin';
|
||||
}
|
||||
|
||||
function requireAdmin(): void
|
||||
{
|
||||
checkSession();
|
||||
|
||||
if (!isAdmin()) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
echo 'Acceso denegado';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentUserId(): int
|
||||
{
|
||||
return $_SESSION['user_id'] ?? 0;
|
||||
}
|
||||
|
||||
function getCurrentUsername(): string
|
||||
{
|
||||
return $_SESSION['username'] ?? '';
|
||||
}
|
||||
|
||||
function getCurrentUserRole(): string
|
||||
{
|
||||
return $_SESSION['role'] ?? 'guest';
|
||||
}
|
||||
69
includes/translation_helper.php
Executable file
69
includes/translation_helper.php
Executable file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/src/Translate.php';
|
||||
|
||||
function translateText(string $text, string $sourceLang, string $targetLang): ?string
|
||||
{
|
||||
$translator = new \src\Translate();
|
||||
return $translator->translate($text, $sourceLang, $targetLang);
|
||||
}
|
||||
|
||||
function detectLanguage(string $text): ?string
|
||||
{
|
||||
$translator = new \src\Translate();
|
||||
return $translator->detectLanguage($text);
|
||||
}
|
||||
|
||||
function getActiveLanguages(): array
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->query("SELECT * FROM supported_languages WHERE is_active = 1 ORDER BY language_name");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
function queueTranslation(string $platform, int $chatId, int $userId, string $text, string $sourceLang): int
|
||||
{
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO translation_queue
|
||||
(platform, chat_id, user_id, text_to_translate, source_lang, status, attempts, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, 'pending', 0, NOW())
|
||||
");
|
||||
$stmt->execute([$platform, $chatId, $userId, $text, $sourceLang]);
|
||||
|
||||
return (int) $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
function translateAndSendToPlatform(string $platform, int $chatId, string $htmlContent): void
|
||||
{
|
||||
$translator = new \src\Translate();
|
||||
|
||||
$converterClass = $platform === 'discord'
|
||||
? \Discord\Converters\HtmlToDiscordMarkdownConverter::class
|
||||
: \Telegram\Converters\HtmlToTelegramHtmlConverter::class;
|
||||
|
||||
$converter = new $converterClass();
|
||||
$textContent = strip_tags($converter->convert($htmlContent));
|
||||
|
||||
$sourceLang = $translator->detectLanguage($textContent) ?? 'es';
|
||||
|
||||
$languages = getActiveLanguages();
|
||||
$targetLangs = array_filter($languages, fn($l) => $l['language_code'] !== $sourceLang);
|
||||
|
||||
$translations = [];
|
||||
foreach ($targetLangs as $lang) {
|
||||
$translations[$lang['language_code']] = $translator->translate($textContent, $sourceLang, $lang['language_code']);
|
||||
}
|
||||
|
||||
if ($platform === 'discord') {
|
||||
$sender = new \Discord\DiscordSender();
|
||||
$actions = new \Discord\Actions\DiscordActions();
|
||||
$actions->sendWithTranslation((string)$chatId, $htmlContent, $translations);
|
||||
} else {
|
||||
$sender = new \Telegram\TelegramSender();
|
||||
$actions = new \Telegram\Actions\TelegramActions();
|
||||
$actions->sendWithTranslation((int)$chatId, $htmlContent, $translations);
|
||||
}
|
||||
}
|
||||
40
includes/url_helper.php
Executable file
40
includes/url_helper.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
function getBaseUrl(): string
|
||||
{
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
|
||||
return "{$protocol}://{$host}";
|
||||
}
|
||||
|
||||
function getAppUrl(string $path = ''): string
|
||||
{
|
||||
return getBaseUrl() . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
function site_url(string $path = ''): string
|
||||
{
|
||||
return getAppUrl($path);
|
||||
}
|
||||
|
||||
function asset(string $path): string
|
||||
{
|
||||
return getAppUrl('assets/' . ltrim($path, '/'));
|
||||
}
|
||||
|
||||
function url(string $path): string
|
||||
{
|
||||
return getAppUrl($path);
|
||||
}
|
||||
|
||||
function redirect(string $path): void
|
||||
{
|
||||
header('Location: ' . site_url($path));
|
||||
exit;
|
||||
}
|
||||
|
||||
function current_url(): string
|
||||
{
|
||||
return getBaseUrl() . ($_SERVER['REQUEST_URI'] ?? '/');
|
||||
}
|
||||
Reference in New Issue
Block a user