Bot Discord - Commit completo con todos los cambios
This commit is contained in:
84
admin/activity.php
Executable file
84
admin/activity.php
Executable file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
// Only admins can access this page
|
||||
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||
header("Location: ../index.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
$limit = 25;
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// Get total number of records
|
||||
$total_stmt = $pdo->query("SELECT COUNT(*) FROM activity_log");
|
||||
$total_records = $total_stmt->fetchColumn();
|
||||
$total_pages = ceil($total_records / $limit);
|
||||
|
||||
// Fetch activity logs
|
||||
$stmt = $pdo->prepare("SELECT * FROM activity_log ORDER BY timestamp DESC LIMIT :limit OFFSET :offset");
|
||||
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Registro de Actividad</h1>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Usuario</th>
|
||||
<th>Acción</th>
|
||||
<th>Detalles</th>
|
||||
<th>Fecha y Hora</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($logs)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No hay registros de actividad.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($logs as $log): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($log['username'] ?? 'N/A'); ?></td>
|
||||
<td><?php echo htmlspecialchars($log['action']); ?></td>
|
||||
<td><?php echo htmlspecialchars($log['details'] ?? 'N/A'); ?></td>
|
||||
<td><?php echo htmlspecialchars(date('d/m/Y H:i:s', strtotime($log['timestamp']))); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination justify-content-center">
|
||||
<?php if ($page > 1): ?>
|
||||
<li class="page-item"><a class="page-link" href="?page=<?php echo $page - 1; ?>">Anterior</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
||||
<li class="page-item <?php echo ($i == $page) ? 'active' : ''; ?>">
|
||||
<a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
|
||||
</li>
|
||||
<?php endfor; ?>
|
||||
|
||||
<?php if ($page < $total_pages): ?>
|
||||
<li class="page-item"><a class="page-link" href="?page=<?php echo $page + 1; ?>">Siguiente</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
54
admin/comandos.php
Executable file
54
admin/comandos.php
Executable file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
// Fetch recurrent messages with commands
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT name, telegram_command FROM recurrent_messages WHERE telegram_command IS NOT NULL AND telegram_command != '' ORDER BY name ASC");
|
||||
$commands = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
die("Error: No se pudieron cargar los comandos.");
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4" data-translate="true">Lista de Comandos de Telegram</h1>
|
||||
<p class="text-muted" data-translate="true">Esta es una lista de todos los comandos de plantilla que has configurado. Los usuarios pueden usar estos comandos en Telegram para recibir el mensaje correspondiente.</p>
|
||||
|
||||
<div class="card card-body p-4">
|
||||
<?php if (empty($commands)): ?>
|
||||
<div class="alert alert-info text-center" role="alert">
|
||||
<i class="bi bi-info-circle-fill me-2"></i>
|
||||
<span data-translate="true">No has configurado ningún comando de Telegram todavía.</span>
|
||||
<br>
|
||||
<span data-translate="true">Puedes asignar comandos a tus plantillas desde la página de</span>
|
||||
<a href="recurrentes.php" class="alert-link" data-translate="true">Mensajes Recurrentes</a>.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th scope="col" data-translate="true">Comando de Telegram</th>
|
||||
<th scope="col" data-translate="true">Nombre de la Plantilla</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($commands as $command): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<code class="fs-5">#<?php echo htmlspecialchars($command['telegram_command']); ?></code>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($command['name']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
45
admin/get_user_groups.php
Executable file
45
admin/get_user_groups.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Admin-only access
|
||||
if ($_SESSION['role'] !== 'admin') {
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$recipientId = $_GET['recipient_id'] ?? null;
|
||||
|
||||
if (!$recipientId) {
|
||||
echo json_encode(['error' => 'Recipient ID missing']);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
// Obtener el nombre del usuario
|
||||
$stmt = $pdo->prepare("SELECT name FROM recipients WHERE id = ?");
|
||||
$stmt->execute([$recipientId]);
|
||||
$userName = $stmt->fetchColumn();
|
||||
|
||||
// Obtener los grupos a los que pertenece el usuario
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT tgm.chat_id, r.name as group_name
|
||||
FROM telegram_group_members tgm
|
||||
JOIN recipients r ON tgm.chat_id = r.platform_id AND r.platform = 'telegram' AND r.type = 'channel'
|
||||
WHERE tgm.recipient_id = ?
|
||||
");
|
||||
$stmt->execute([$recipientId]);
|
||||
$groups = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'userName' => $userName, 'groups' => $groups]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error fetching user groups: " . $e->getMessage());
|
||||
echo json_encode(['error' => 'Database error']);
|
||||
} catch (Exception $e) {
|
||||
error_log("Error: " . $e->getMessage());
|
||||
echo json_encode(['error' => 'Server error']);
|
||||
}
|
||||
?>
|
||||
214
admin/languages.php
Executable file
214
admin/languages.php
Executable file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
// Solo para administradores
|
||||
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pageTitle = 'Gestionar Idiomas de Traducción';
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT * FROM supported_languages ORDER BY language_name ASC");
|
||||
$languages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$languages = [];
|
||||
$errorMessage = "Error al cargar los idiomas: " . $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Gestionar Idiomas de Traducción</h1>
|
||||
<p class="text-muted">Activa o desactiva los idiomas a los que el bot traducirá automáticamente los mensajes.</p>
|
||||
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($errorMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Idiomas Soportados</h5>
|
||||
<button id="sync-languages-btn" class="btn btn-secondary btn-sm">
|
||||
<i class="bi bi-arrow-repeat me-1"></i> Sincronizar con LibreTranslate
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="language-list" class="list-group">
|
||||
<?php if (empty($languages)): ?>
|
||||
<div class="list-group-item">No se encontraron idiomas.</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($languages as $lang): ?>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flag-container me-2" style="min-width: 40px;">
|
||||
<span class="fs-4 flag-emoji"><?= htmlspecialchars($lang['flag_emoji'] ?? '') ?></span>
|
||||
<div class="edit-flag-form d-none">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control flag-input" value="<?= htmlspecialchars($lang['flag_emoji'] ?? '') ?>" placeholder=" पेस्ट">
|
||||
<button class="btn btn-success btn-sm save-flag-btn" data-lang-id="<?= $lang['id'] ?>">✓</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<i class="bi bi-pencil-square edit-flag-btn me-2" style="cursor: pointer;" data-lang-id="<?= $lang['id'] ?>"></i>
|
||||
<div>
|
||||
<span class="fw-bold"><?= htmlspecialchars($lang['language_name']) ?></span>
|
||||
<small class="text-muted">(<?= htmlspecialchars($lang['language_code']) ?>)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input language-toggle" type="checkbox" role="switch"
|
||||
id="lang-toggle-<?= $lang['id'] ?>"
|
||||
data-lang-id="<?= $lang['id'] ?>"
|
||||
<?= $lang['is_active'] ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="lang-toggle-<?= $lang['id'] ?>"></label>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="alert-container" class="position-fixed bottom-0 end-0 p-3" style="z-index: 11"></div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const languageToggles = document.querySelectorAll('.language-toggle');
|
||||
const syncBtn = document.getElementById('sync-languages-btn');
|
||||
|
||||
if (syncBtn) {
|
||||
syncBtn.addEventListener('click', function () {
|
||||
const originalHtml = this.innerHTML;
|
||||
this.disabled = true;
|
||||
this.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Sincronizando...';
|
||||
|
||||
fetch('sync_languages.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert(`Sincronización completada. Se añadieron ${data.new_languages} nuevos idiomas.`, 'success');
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
} else {
|
||||
showAlert('Error en la sincronización: ' + data.error, 'danger');
|
||||
this.disabled = false;
|
||||
this.innerHTML = originalHtml;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showAlert('Error de red durante la sincronización.', 'danger');
|
||||
this.disabled = false;
|
||||
this.innerHTML = originalHtml;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
languageToggles.forEach(toggle => {
|
||||
toggle.addEventListener('change', function () {
|
||||
const langId = this.dataset.langId;
|
||||
const isActive = this.checked;
|
||||
|
||||
fetch('update_language_status.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
body: JSON.stringify({ id: langId, is_active: isActive })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert('Estado del idioma actualizado con éxito.', 'success');
|
||||
} else {
|
||||
showAlert('Error: ' + data.error, 'danger');
|
||||
// Revertir el cambio visual si falla la actualización
|
||||
this.checked = !isActive;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showAlert('Error de red al actualizar el idioma.', 'danger');
|
||||
this.checked = !isActive;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Lógica para editar la bandera
|
||||
const languageList = document.getElementById('language-list');
|
||||
if (languageList) {
|
||||
languageList.addEventListener('click', function(e) {
|
||||
// Botón de editar
|
||||
if (e.target.classList.contains('edit-flag-btn')) {
|
||||
const listItem = e.target.closest('.list-group-item');
|
||||
listItem.querySelector('.flag-emoji').classList.add('d-none');
|
||||
e.target.classList.add('d-none');
|
||||
listItem.querySelector('.edit-flag-form').classList.remove('d-none');
|
||||
listItem.querySelector('.flag-input').focus();
|
||||
}
|
||||
|
||||
// Botón de guardar
|
||||
if (e.target.classList.contains('save-flag-btn')) {
|
||||
const langId = e.target.dataset.langId;
|
||||
const listItem = e.target.closest('.list-group-item');
|
||||
const input = listItem.querySelector('.flag-input');
|
||||
const newEmoji = input.value;
|
||||
|
||||
fetch('update_language_flag.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
body: JSON.stringify({ id: langId, flag_emoji: newEmoji })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const flagEmojiSpan = listItem.querySelector('.flag-emoji');
|
||||
flagEmojiSpan.textContent = newEmoji;
|
||||
flagEmojiSpan.classList.remove('d-none');
|
||||
listItem.querySelector('.edit-flag-form').classList.add('d-none');
|
||||
listItem.querySelector('.edit-flag-btn').classList.remove('d-none');
|
||||
showAlert('Bandera actualizada.', 'success');
|
||||
} else {
|
||||
showAlert('Error: ' + data.error, 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showAlert('Error de red al actualizar la bandera.', 'danger');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showAlert(message, type = 'success') {
|
||||
const alertContainer = document.getElementById('alert-container');
|
||||
const alert = document.createElement('div');
|
||||
alert.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alert.role = 'alert';
|
||||
alert.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
`;
|
||||
alertContainer.appendChild(alert);
|
||||
|
||||
setTimeout(() => {
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
538
admin/recipients.php
Executable file
538
admin/recipients.php
Executable file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/activity_logger.php';
|
||||
|
||||
// Admin-only access
|
||||
if ($_SESSION['role'] !== 'admin') {
|
||||
header('Location: ../index.php?error=unauthorized');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialize variables for edit mode
|
||||
$edit_mode = false;
|
||||
$edit_recipient = [
|
||||
'id' => null,
|
||||
'name' => '',
|
||||
'platform_id' => '',
|
||||
'type' => 'channel',
|
||||
'platform' => 'discord',
|
||||
'language_code' => 'es'
|
||||
];
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$current_user_id = $_SESSION['user_id'];
|
||||
$current_username = $_SESSION['username'];
|
||||
$action = $_POST['action'] ?? null;
|
||||
|
||||
// Action: Add or Update Recipient
|
||||
if (isset($_POST['add_recipient']) || isset($_POST['update_recipient'])) {
|
||||
$platform = $_POST['platform'] ?? 'discord';
|
||||
$type = $_POST['type'] ?? 'channel';
|
||||
$name = $_POST['name'] ?? '';
|
||||
$platform_id = $_POST['platform_id'] ?? '';
|
||||
$language_code = $_POST['language_code'] ?? 'es';
|
||||
$id = $_POST['id'] ?? null;
|
||||
|
||||
if (empty($name) || empty($platform_id) || empty($type) || empty($platform)) {
|
||||
$error = "Todos los campos son obligatorios.";
|
||||
} elseif (!is_numeric($platform_id)) {
|
||||
$error = "El ID de Plataforma debe ser un número.";
|
||||
} else {
|
||||
try {
|
||||
if (isset($_POST['update_recipient'])) { // UPDATE
|
||||
$stmt = $pdo->prepare("UPDATE recipients SET name = ?, platform_id = ?, type = ?, platform = ?, language_code = ? WHERE id = ?");
|
||||
$stmt->execute([$name, $platform_id, $type, $platform, $language_code, $id]);
|
||||
$details = 'Admin ' . $current_username . ' updated recipient: ' . $name . ' (' . $platform . ':' . $platform_id . ')';
|
||||
log_activity($current_user_id, 'Recipient Updated', $details);
|
||||
header('Location: recipients.php?success=updated');
|
||||
exit();
|
||||
} else { // ADD
|
||||
$stmt = $pdo->prepare("INSERT INTO recipients (name, platform_id, type, platform, language_code) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$name, $platform_id, $type, $platform, $language_code]);
|
||||
$new_recipient_id = $pdo->lastInsertId();
|
||||
$details = 'Admin ' . $current_username . ' added new recipient: ' . $name . ' (' . $platform . ':' . $platform_id . ')';
|
||||
log_activity($current_user_id, 'Recipient Added', $details);
|
||||
$success = "Destinatario añadido con éxito.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
if ($e->errorInfo[1] == 1062) {
|
||||
$error = "El ID de Plataforma ('$platform_id') ya existe.";
|
||||
} else {
|
||||
$error = "Error en la base de datos: " . $e->getMessage();
|
||||
}
|
||||
// Keep form data on error
|
||||
$edit_mode = isset($_POST['update_recipient']);
|
||||
$edit_recipient = ['id' => $id, 'name' => $name, 'platform_id' => $platform_id, 'type' => $type, 'platform' => $platform, 'language_code' => $language_code];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Action: Delete Single Recipient
|
||||
elseif (isset($_POST['delete_recipient'])) {
|
||||
$id_to_delete = $_POST['id_to_delete'];
|
||||
try {
|
||||
$stmt_recipient = $pdo->prepare("SELECT name, platform, platform_id FROM recipients WHERE id = ?");
|
||||
$stmt_recipient->execute([$id_to_delete]);
|
||||
$recipient_info = $stmt_recipient->fetch(PDO::FETCH_ASSOC);
|
||||
$details = 'Admin ' . $current_username . ' deleted recipient: ' . ($recipient_info['name'] ?? 'Unknown') . ' (' . ($recipient_info['platform'] ?? 'N/A') . ':' . ($recipient_info['platform_id'] ?? 'N/A') . ')';
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM recipients WHERE id = ?");
|
||||
$stmt->execute([$id_to_delete]);
|
||||
log_activity($current_user_id, 'Recipient Deleted', $details);
|
||||
$success = "Destinatario eliminado con éxito.";
|
||||
} catch (PDOException $e) {
|
||||
$error = "Error al eliminar. Es posible que el destinatario esté en uso.";
|
||||
}
|
||||
}
|
||||
// Action: Kick Telegram User
|
||||
elseif ($action === 'kick_telegram_user') {
|
||||
$recipient_id_to_kick = $_POST['recipient_id_to_kick'] ?? null;
|
||||
$chat_id_to_kick_from = $_POST['chat_id_to_kick_from'] ?? null;
|
||||
|
||||
if ($recipient_id_to_kick && $chat_id_to_kick_from) {
|
||||
try {
|
||||
// Get recipient's platform_id (Telegram user ID)
|
||||
$stmt = $pdo->prepare("SELECT platform_id, name FROM recipients WHERE id = ? AND platform = 'telegram' AND type = 'user'");
|
||||
$stmt->execute([$recipient_id_to_kick]);
|
||||
$recipient_info = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$telegram_user_id = $recipient_info['platform_id'] ?? null;
|
||||
$recipient_name = $recipient_info['name'] ?? 'Unknown';
|
||||
|
||||
if ($telegram_user_id) {
|
||||
// Get bot token
|
||||
$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '';
|
||||
if (empty($botToken)) {
|
||||
throw new Exception("Token de bot de Telegram no configurado.");
|
||||
}
|
||||
|
||||
// Telegram API URL for banning a chat member
|
||||
$telegramApiUrl = "https://api.telegram.org/bot{$botToken}/banChatMember";
|
||||
$params = [
|
||||
'chat_id' => $chat_id_to_kick_from,
|
||||
'user_id' => $telegram_user_id,
|
||||
'until_date' => time() + 30 // Ban for 30 seconds to ensure they are removed, then they can rejoin
|
||||
];
|
||||
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($params),
|
||||
],
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($telegramApiUrl, false, $context);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if ($response && $response['ok']) {
|
||||
// If successful, unban immediately to allow re-entry
|
||||
$unbanTelegramApiUrl = "https://api.telegram.org/bot{$botToken}/unbanChatMember";
|
||||
$unbanParams = [
|
||||
'chat_id' => $chat_id_to_kick_from,
|
||||
'user_id' => $telegram_user_id,
|
||||
'only_if_banned' => true // Only try to unban if they are actually banned
|
||||
];
|
||||
$unbanOptions = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($unbanParams),
|
||||
],
|
||||
];
|
||||
$unbanContext = stream_context_create($unbanOptions);
|
||||
$unbanResult = file_get_contents($unbanTelegramApiUrl, false, $unbanContext);
|
||||
$unbanResponse = json_decode($unbanResult, true);
|
||||
|
||||
if ($unbanResponse && $unbanResponse['ok']) {
|
||||
$success = "Usuario de Telegram expulsado del grupo (permite reingreso) y eliminado de la base de datos.";
|
||||
} else {
|
||||
// Log unban error but still proceed with local deletion as kick was successful
|
||||
error_log("Error al desbanear usuario de Telegram después de la expulsión: " . ($unbanResponse['description'] ?? 'Error desconocido'));
|
||||
$success = "Usuario de Telegram expulsado del grupo (error al permitir reingreso) y eliminado de la base de datos.";
|
||||
}
|
||||
|
||||
// Delete from local DB regardless of unban success, as the kick itself was successful
|
||||
$stmt = $pdo->prepare("DELETE FROM recipients WHERE id = ?");
|
||||
$stmt->execute([$recipient_id_to_kick]);
|
||||
$details = 'Admin ' . $current_username . ' kicked Telegram user: ' . $recipient_name . ' (ID: ' . $telegram_user_id . ') from group ID: ' . $chat_id_to_kick_from;
|
||||
log_activity($current_user_id, 'Telegram User Kicked', $details);
|
||||
|
||||
} else {
|
||||
$error = "Error al expulsar usuario de Telegram: " . ($response['description'] ?? 'Error desconocido');
|
||||
}
|
||||
} else {
|
||||
$error = "Usuario de Telegram no encontrado o no es un usuario válido para expulsar.";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al procesar la expulsión: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "Faltan parámetros para expulsar al usuario de Telegram.";
|
||||
}
|
||||
}
|
||||
// Action: Delete Multiple Recipients
|
||||
elseif ($action === 'delete_selected' && !empty($_POST['selected_recipients'])) {
|
||||
$deleted_count = 0;
|
||||
$error_count = 0;
|
||||
|
||||
foreach ($_POST['selected_recipients'] as $recipient_id) {
|
||||
try {
|
||||
$stmt_recipient = $pdo->prepare("SELECT name, platform, platform_id FROM recipients WHERE id = ?");
|
||||
$stmt_recipient->execute([$recipient_id]);
|
||||
$recipient_info = $stmt_recipient->fetch(PDO::FETCH_ASSOC);
|
||||
$details = 'Admin ' . $current_username . ' deleted recipient: ' . ($recipient_info['name'] ?? 'Unknown') . ' (' . ($recipient_info['platform'] ?? 'N/A') . ':' . ($recipient_info['platform_id'] ?? 'N/A') . ')';
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM recipients WHERE id = ?");
|
||||
$stmt->execute([$recipient_id]);
|
||||
log_activity($current_user_id, 'Recipient Deleted', $details);
|
||||
$deleted_count++;
|
||||
} catch (PDOException $e) {
|
||||
$error_count++;
|
||||
error_log("Error al eliminar destinatario ID $recipient_id: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ($deleted_count > 0) {
|
||||
$success = "Se eliminaron $deleted_count destinatarios correctamente.";
|
||||
if ($error_count > 0) {
|
||||
$error = "Hubo errores al eliminar $error_count destinatarios.";
|
||||
}
|
||||
header('Location: recipients.php?success=deleted_multiple&deleted=' . $deleted_count . '&errors=' . $error_count);
|
||||
exit();
|
||||
} else if ($error_count > 0) {
|
||||
$error = "No se pudo eliminar ningún destinatario. Por favor, inténtalo de nuevo.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle entering edit mode via GET request
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
|
||||
$edit_mode = true;
|
||||
$stmt = $pdo->prepare("SELECT * FROM recipients WHERE id = ?");
|
||||
$stmt->execute([$_GET['id']]);
|
||||
$recipient_to_edit = $stmt->fetch();
|
||||
if ($recipient_to_edit) {
|
||||
$edit_recipient = $recipient_to_edit;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all recipients to display
|
||||
$recipients = $pdo->query("SELECT * FROM recipients ORDER BY platform, type, name ASC")->fetchAll();
|
||||
|
||||
$pageTitle = 'Gestionar Destinatarios';
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
|
||||
// Display feedback messages
|
||||
if (isset($error)) echo "<div class='alert alert-danger'>$error</div>";
|
||||
if (isset($success)) echo "<div class='alert alert-success'>$success</div>";
|
||||
|
||||
if (isset($_GET['success'])) {
|
||||
if ($_GET['success'] === 'deleted_multiple') {
|
||||
$deleted = isset($_GET['deleted']) ? (int)$_GET['deleted'] : 0;
|
||||
$errors = isset($_GET['errors']) ? (int)$_GET['errors'] : 0;
|
||||
|
||||
if ($deleted > 0) {
|
||||
echo '<div class="alert alert-success">Se eliminaron ' . $deleted . ' destinatarios correctamente.</div>';
|
||||
}
|
||||
if ($errors > 0) {
|
||||
echo '<div class="alert alert-danger">Hubo errores al eliminar ' . $errors . ' destinatarios.</div>';
|
||||
}
|
||||
} else {
|
||||
echo "<div class='alert alert-success'>Operación completada con éxito.</div>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4" data-translate="true">Gestionar Destinatarios</h1>
|
||||
|
||||
<!-- Add/Edit Recipient Form -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0" data-translate="true"><?= $edit_mode ? 'Editar Destinatario' : 'Añadir Nuevo Destinatario' ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="recipients.php" method="POST">
|
||||
<?php if ($edit_mode): ?><input type="hidden" name="id" value="<?= $edit_recipient['id'] ?>"><?php endif; ?>
|
||||
<div class="row align-items-end">
|
||||
<div class="col-md-2 mb-3">
|
||||
<label for="platform" class="form-label" data-translate="true">Plataforma</label>
|
||||
<select class="form-select" id="platform" name="platform" required>
|
||||
<option value="discord" <?= ($edit_recipient['platform'] === 'discord') ? 'selected' : '' ?> data-translate="true">Discord</option>
|
||||
<option value="telegram" <?= ($edit_recipient['platform'] === 'telegram') ? 'selected' : '' ?> data-translate="true">Telegram</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="name" class="form-label" data-translate="true">Nombre (identificador)</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?= htmlspecialchars($edit_recipient['name']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label for="platform_id" class="form-label" data-translate="true">ID de Plataforma</label>
|
||||
<input type="text" class="form-control" id="platform_id" name="platform_id" value="<?= htmlspecialchars($edit_recipient['platform_id']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label for="type" class="form-label" data-translate="true">Tipo</label>
|
||||
<select class="form-select" id="type" name="type" required>
|
||||
<option value="channel" <?= ($edit_recipient['type'] === 'channel') ? 'selected' : '' ?> data-translate="true">Canal/Grupo</option>
|
||||
<option value="user" <?= ($edit_recipient['type'] === 'user') ? 'selected' : '' ?> data-translate="true">Usuario</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-1 mb-3">
|
||||
<label for="language_code" class="form-label" data-translate="true">Idioma</label>
|
||||
<select class="form-select" id="language_code" name="language_code" required>
|
||||
<option value="es" <?= ($edit_recipient['language_code'] === 'es') ? 'selected' : '' ?>>ES</option>
|
||||
<option value="en" <?= ($edit_recipient['language_code'] === 'en') ? 'selected' : '' ?>>EN</option>
|
||||
<option value="fr" <?= ($edit_recipient['language_code'] === 'fr') ? 'selected' : '' ?>>FR</option>
|
||||
<option value="de" <?= ($edit_recipient['language_code'] === 'de') ? 'selected' : '' ?>>DE</option>
|
||||
<option value="it" <?= ($edit_recipient['language_code'] === 'it') ? 'selected' : '' ?>>IT</option>
|
||||
<option value="pt" <?= ($edit_recipient['language_code'] === 'pt') ? 'selected' : '' ?>>PT</option>
|
||||
<option value="ru" <?= ($edit_recipient['language_code'] === 'ru') ? 'selected' : '' ?>>RU</option>
|
||||
<option value="ja" <?= ($edit_recipient['language_code'] === 'ja') ? 'selected' : '' ?>>JA</option>
|
||||
<option value="zh" <?= ($edit_recipient['language_code'] === 'zh') ? 'selected' : '' ?>>ZH</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<?php if ($edit_mode): ?>
|
||||
<button type="submit" name="update_recipient" class="btn btn-success w-100">
|
||||
<i class="bi bi-save"></i> <span data-translate="true">Guardar</span>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="add_recipient" class="btn btn-primary w-100">
|
||||
<i class="bi bi-plus-circle"></i> <span data-translate="true">Añadir</span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($edit_mode): ?>
|
||||
<div class="col-md-12"><a href="recipients.php" class="btn btn-sm btn-outline-secondary" data-translate="true">Cancelar Edición</a></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recipients List -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0" data-translate="true">Lista de Destinatarios</h5>
|
||||
<div>
|
||||
<button type="button" id="selectAllBtn" class="btn btn-sm btn-outline-secondary me-2">
|
||||
<i class="bi bi-check2-square"></i> <span data-translate="true">Seleccionar todo</span>
|
||||
</button>
|
||||
<button type="button" id="deleteSelectedBtn" class="btn btn-sm btn-danger" disabled>
|
||||
<i class="bi bi-trash"></i> <span data-translate="true">Eliminar seleccionados</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="recipientsForm" action="" method="POST" onsubmit="return confirm('¿Estás seguro de que quieres eliminar los destinatarios seleccionados?');">
|
||||
<input type="hidden" name="action" value="delete_selected">
|
||||
<input type="hidden" name="confirm_message" value="¿Estás seguro de que quieres eliminar los destinatarios seleccionados?" data-translate-confirm="¿Estás seguro de que quieres eliminar los destinatarios seleccionados?">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th width="40"><input type="checkbox" id="selectAll"></th>
|
||||
<th data-translate="true">Plataforma</th>
|
||||
<th data-translate="true">Nombre</th>
|
||||
<th data-translate="true">ID de Plataforma</th>
|
||||
<th data-translate="true">Tipo</th>
|
||||
<th data-translate="true">Idioma</th>
|
||||
<th data-translate="true">Añadido en</th>
|
||||
<th class="text-center" data-translate="true">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($recipients as $recipient): ?>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="selected_recipients[]" value="<?= $recipient['id'] ?>" class="recipient-checkbox"></td>
|
||||
<td>
|
||||
<span class="badge <?= $recipient['platform'] === 'discord' ? 'bg-primary' : 'bg-info' ?>">
|
||||
<?= htmlspecialchars(ucfirst($recipient['platform'])) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($recipient['name']) ?></td>
|
||||
<td><?= htmlspecialchars($recipient['platform_id']) ?></td>
|
||||
<td><?= htmlspecialchars(ucfirst($recipient['type'])) ?></td>
|
||||
<td><span class="badge bg-secondary"><?= strtoupper(htmlspecialchars($recipient['language_code'] ?? 'es')) ?></span></td>
|
||||
<td><?= date('d/m/Y H:i', strtotime($recipient['created_at'])) ?></td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="?action=edit&id=<?= $recipient['id'] ?>" class="btn btn-sm btn-primary" title="Editar">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form action="recipients.php" method="POST" onsubmit="return confirm(this.querySelector('[data-translate-confirm]').getAttribute('data-translate-confirm'));" class="d-inline ms-1">
|
||||
<input type="hidden" name="id_to_delete" value="<?= $recipient['id'] ?>">
|
||||
<input type="hidden" name="confirm_message" value="¿Estás seguro de que quieres eliminar este destinatario?" data-translate-confirm="¿Estás seguro de que quieres eliminar este destinatario?">
|
||||
<button type="submit" name="delete_recipient" class="btn btn-sm btn-danger" title="Eliminar" data-translate-title="true">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
<?php if ($recipient['platform'] === 'telegram' && $recipient['type'] === 'user'): ?>
|
||||
<button type="button" class="btn btn-sm btn-warning ms-1 kick-telegram-user-btn"
|
||||
data-bs-toggle="modal" data-bs-target="#kickUserModal"
|
||||
data-recipient-id="<?= $recipient['id'] ?>"
|
||||
title="Expulsar de Telegram y Eliminar" data-translate-title="true">
|
||||
<i class="bi bi-person-x"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
|
||||
<!-- Modal para Expulsar Usuario de Telegram -->
|
||||
<div class="modal fade" id="kickUserModal" tabindex="-1" aria-labelledby="kickUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="kickUserModalLabel">Expulsar Usuario de Telegram</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form id="kickUserForm" action="recipients.php" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="action" value="kick_telegram_user">
|
||||
<input type="hidden" name="recipient_id_to_kick" id="modalRecipientId">
|
||||
<p>Selecciona el grupo del que deseas expulsar a <strong id="modalUserName"></strong>:</p>
|
||||
<div id="groupListContainer">
|
||||
<!-- Los grupos se cargarán aquí dinámicamente -->
|
||||
<p class="text-muted">Cargando grupos...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="submit" class="btn btn-danger" id="confirmKickBtn" disabled>Expulsar y Eliminar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Selección múltiple
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const recipientCheckboxes = document.querySelectorAll('.recipient-checkbox');
|
||||
const deleteSelectedBtn = document.getElementById('deleteSelectedBtn');
|
||||
const selectAllBtn = document.getElementById('selectAllBtn');
|
||||
|
||||
// Seleccionar/deseleccionar todos los checkboxes
|
||||
if (selectAllCheckbox) {
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const isChecked = this.checked;
|
||||
recipientCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = isChecked;
|
||||
});
|
||||
updateDeleteButton();
|
||||
});
|
||||
}
|
||||
|
||||
// Botón "Seleccionar todo"
|
||||
if (selectAllBtn) {
|
||||
selectAllBtn.addEventListener('click', function() {
|
||||
const allChecked = Array.from(recipientCheckboxes).every(checkbox => checkbox.checked);
|
||||
recipientCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = !allChecked;
|
||||
});
|
||||
selectAllCheckbox.checked = !allChecked;
|
||||
updateDeleteButton();
|
||||
});
|
||||
}
|
||||
|
||||
// Actualizar el estado del botón de eliminar seleccionados
|
||||
function updateDeleteButton() {
|
||||
const checkedBoxes = document.querySelectorAll('.recipient-checkbox:checked');
|
||||
deleteSelectedBtn.disabled = checkedBoxes.length === 0;
|
||||
}
|
||||
|
||||
// Actualizar el checkbox "Seleccionar todo" cuando se marcan/desmarcan checkboxes individuales
|
||||
recipientCheckboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
updateDeleteButton();
|
||||
selectAllCheckbox.checked = Array.from(recipientCheckboxes).every(checkbox => checkbox.checked);
|
||||
});
|
||||
});
|
||||
|
||||
// Manejar el clic en el botón de eliminar seleccionados
|
||||
if (deleteSelectedBtn) {
|
||||
deleteSelectedBtn.addEventListener('click', function() {
|
||||
if (confirm('¿Estás seguro de que quieres eliminar los destinatarios seleccionados?')) {
|
||||
document.getElementById('recipientsForm').submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Lógica para el modal de expulsar usuario de Telegram
|
||||
const kickUserModal = document.getElementById('kickUserModal');
|
||||
const modalRecipientId = document.getElementById('modalRecipientId');
|
||||
const modalUserName = document.getElementById('modalUserName');
|
||||
const groupListContainer = document.getElementById('groupListContainer');
|
||||
const confirmKickBtn = document.getElementById('confirmKickBtn');
|
||||
const kickUserForm = document.getElementById('kickUserForm');
|
||||
|
||||
kickUserModal.addEventListener('show.bs.modal', function (event) {
|
||||
const button = event.relatedTarget; // Botón que activó el modal
|
||||
const recipientId = button.getAttribute('data-recipient-id');
|
||||
|
||||
modalRecipientId.value = recipientId;
|
||||
modalUserName.textContent = 'Cargando...';
|
||||
groupListContainer.innerHTML = '<p class="text-muted">Cargando grupos...</p>';
|
||||
confirmKickBtn.disabled = true;
|
||||
|
||||
// Cargar los grupos del usuario vía AJAX
|
||||
fetch(`get_user_groups.php?recipient_id=${recipientId}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
modalUserName.textContent = data.userName;
|
||||
if (data.groups.length > 0) {
|
||||
let groupsHtml = '';
|
||||
data.groups.forEach(group => {
|
||||
groupsHtml += `
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="chat_id_to_kick_from" id="group_${group.chat_id}" value="${group.chat_id}">
|
||||
<label class="form-check-label" for="group_${group.chat_id}">
|
||||
${group.group_name} (ID: ${group.chat_id})
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
groupListContainer.innerHTML = groupsHtml;
|
||||
|
||||
// Habilitar el botón de confirmar cuando se selecciona un radio
|
||||
groupListContainer.querySelectorAll('input[type="radio"]').forEach(radio => {
|
||||
radio.addEventListener('change', () => {
|
||||
confirmKickBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
groupListContainer.innerHTML = '<p class="text-info">Este usuario no está registrado en ningún grupo de Telegram.</p>';
|
||||
}
|
||||
} else {
|
||||
groupListContainer.innerHTML = `<p class="text-danger">Error al cargar grupos: ${data.error}</p>`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching groups:', error);
|
||||
groupListContainer.innerHTML = '<p class="text-danger">Error de red al cargar grupos.</p>';
|
||||
});
|
||||
});
|
||||
|
||||
// Confirmación final antes de enviar el formulario de expulsión
|
||||
kickUserForm.addEventListener('submit', function(event) {
|
||||
if (!confirm('¿Estás seguro de que quieres expulsar a este usuario del grupo seleccionado y eliminarlo de la base de datos?')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
65
admin/set_test_webhook.php
Executable file
65
admin/set_test_webhook.php
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
// Cargar configuración del entorno de pruebas
|
||||
$envFile = __DIR__ . '/.env.pruebas';
|
||||
if (!file_exists($envFile)) {
|
||||
die("Error: No se encontró el archivo de configuración de pruebas (.env.pruebas)\n");
|
||||
}
|
||||
|
||||
// Cargar variables de entorno
|
||||
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (strpos(trim($line), '#') === 0) continue;
|
||||
list($name, $value) = explode('=', $line, 2);
|
||||
$name = trim($name);
|
||||
$value = trim($value, "'\" \t\n\r\0\x0B");
|
||||
if (!empty($name)) $_ENV[$name] = $value;
|
||||
}
|
||||
|
||||
// Configuración
|
||||
$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '';
|
||||
$webhookToken = $_ENV['TELEGRAM_WEBHOOK_TOKEN'] ?? 'webhook_secure_token_12345';
|
||||
$webhookUrl = 'https://pruebaspons.duckdns.org/telegram_bot_webhook.php?auth_token=' . urlencode($webhookToken);
|
||||
|
||||
if (empty($botToken)) die("Error: No se configuró TELEGRAM_BOT_TOKEN\n");
|
||||
|
||||
echo "Configurando webhook para pruebas...\nURL: $webhookUrl\n";
|
||||
|
||||
// Configurar webhook
|
||||
$apiUrl = "https://api.telegram.org/bot{$botToken}/setWebhook";
|
||||
$postData = ['url' => $webhookUrl, 'allowed_updates' => json_encode(['message', 'callback_query'])];
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// Mostrar resultados
|
||||
echo "\nRespuesta de la API de Telegram (HTTP $httpCode):\n";
|
||||
if ($error) {
|
||||
echo "Error: $error\n";
|
||||
} else {
|
||||
$result = json_decode($response, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
if ($result['ok'] ?? false) {
|
||||
echo "✅ Webhook configurado correctamente.\n";
|
||||
echo "URL: " . ($result['result']['url'] ?? 'N/A') . "\n";
|
||||
echo "Tiene certificado: " . ($result['result']['has_custom_certificate'] ? 'Sí' : 'No') . "\n";
|
||||
echo "Updates pendientes: " . ($result['result']['pending_update_count'] ?? '0') . "\n";
|
||||
} else {
|
||||
echo "❌ Error al configurar el webhook: " . ($result['description'] ?? 'Error desconocido') . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "Respuesta no válida: " . substr($response, 0, 200) . "...\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\nPara verificar la configuración actual del webhook, ejecuta:\n";
|
||||
echo "curl -s 'https://api.telegram.org/bot{$botToken}/getWebhookInfo' | jq\n\n";
|
||||
66
admin/sync_languages.php
Executable file
66
admin/sync_languages.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
// admin/sync_languages.php
|
||||
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/logger.php';
|
||||
require_once __DIR__ . '/../src/Translate.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Envolver todo en un manejador de errores para capturar hasta los errores fatales
|
||||
register_shutdown_function(function () {
|
||||
$error = error_get_last();
|
||||
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
|
||||
// Limpiar cualquier salida anterior
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
echo json_encode(['success' => false, 'error' => 'Error fatal en el servidor: ' . $error['message'] . ' en ' . $error['file'] . ' línea ' . $error['line']]);
|
||||
}
|
||||
});
|
||||
|
||||
// Solo para administradores
|
||||
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||
echo json_encode(['success' => false, 'error' => 'Acceso denegado.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Verificar que la URL de LibreTranslate esté configurada
|
||||
if (empty($_ENV['LIBRETRANSLATE_URL'])) {
|
||||
throw new Exception("La variable de entorno LIBRETRANSLATE_URL no está configurada en tu archivo .env");
|
||||
}
|
||||
|
||||
$translator = new Translate();
|
||||
$libreLanguages = $translator->getSupportedLanguages();
|
||||
|
||||
if ($libreLanguages === null) {
|
||||
throw new Exception("No se pudo obtener la lista de idiomas de LibreTranslate. Revisa que la URL ('" . htmlspecialchars($_ENV['LIBRETRANSLATE_URL']) . "') sea correcta y que el servicio esté funcionando.");
|
||||
}
|
||||
|
||||
if (empty($libreLanguages)) {
|
||||
throw new Exception("LibreTranslate devolvió una lista de idiomas vacía.");
|
||||
}
|
||||
|
||||
$newLanguagesCount = 0;
|
||||
$sql = "INSERT IGNORE INTO supported_languages (language_code, language_name, is_active) VALUES (?, ?, 0)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
foreach ($libreLanguages as $lang) {
|
||||
if (isset($lang['code']) && isset($lang['name'])) {
|
||||
$stmt->execute([$lang['code'], $lang['name']]);
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$newLanguagesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'new_languages' => $newLanguagesCount]);
|
||||
|
||||
} catch (Throwable $e) { // Captura Throwable para errores y excepciones
|
||||
error_log("Error en sync_languages.php: " . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
408
admin/test_discord_connection.php
Executable file
408
admin/test_discord_connection.php
Executable file
@@ -0,0 +1,408 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../src/DiscordSender.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
// Verificar que el usuario sea administrador
|
||||
if ($_SESSION['role'] !== 'admin') {
|
||||
header('HTTP/1.0 403 Forbidden');
|
||||
die('Acceso denegado. Solo los administradores pueden acceder a esta página.');
|
||||
}
|
||||
|
||||
$message = '';
|
||||
$error = '';
|
||||
$success = '';
|
||||
$discordResponse = null;
|
||||
$permissionsInfo = [];
|
||||
$telegramWebhookInfo = null;
|
||||
|
||||
// Manejar el envío del formulario de prueba
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Verificar si es una solicitud de verificación de permisos de Discord
|
||||
if (isset($_POST['check_permissions']) && !empty($_POST['guild_id'])) {
|
||||
$guildId = trim($_POST['guild_id']);
|
||||
try {
|
||||
$ch = curl_init("https://discord.com/api/v10/guilds/{$guildId}");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bot " . DISCORD_BOT_TOKEN,
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$guild = json_decode($response, true);
|
||||
|
||||
$ch = curl_init("https://discord.com/api/v10/users/@me");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bot " . DISCORD_BOT_TOKEN],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$botInfo = json_decode(curl_exec($ch), true);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($httpCode !== 200 || !isset($botInfo['id'])) {
|
||||
throw new Exception("No se pudo obtener la información del bot de Discord. Verifica que el token sea correcto. Código HTTP: $httpCode");
|
||||
}
|
||||
|
||||
$botId = $botInfo['id'];
|
||||
|
||||
$ch = curl_init("https://discord.com/api/v10/guilds/{$guildId}/roles");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bot " . DISCORD_BOT_TOKEN],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$roles = json_decode(curl_exec($ch), true);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($httpCode === 200 && is_array($roles)) {
|
||||
$ch = curl_init("https://discord.com/api/v10/guilds/{$guildId}/members/{$botId}");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bot " . DISCORD_BOT_TOKEN],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$member = json_decode(curl_exec($ch), true);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($httpCode === 404) {
|
||||
throw new Exception("El bot no está en el servidor de Discord especificado.");
|
||||
} elseif ($httpCode !== 200) {
|
||||
throw new Exception("Error al obtener información del bot en el servidor. Código HTTP: $httpCode");
|
||||
}
|
||||
|
||||
if (isset($member['roles']) && is_array($member['roles'])) {
|
||||
$botRoles = array_filter($roles, fn($role) => in_array($role['id'], $member['roles']));
|
||||
$permissions = 0;
|
||||
foreach ($botRoles as $role) {
|
||||
$permissions |= intval($role['permissions']);
|
||||
}
|
||||
|
||||
$permissionsInfo = [
|
||||
'guild_name' => $guild['name'] ?? 'Desconocido',
|
||||
'bot_has_admin' => ($permissions & 0x8) === 0x8,
|
||||
'can_kick' => ($permissions & 0x2) === 0x2,
|
||||
'can_ban' => ($permissions & 0x4) === 0x4,
|
||||
'permissions_int' => $permissions,
|
||||
'permissions_bin' => decbin($permissions),
|
||||
'roles' => array_column($botRoles, 'name')
|
||||
];
|
||||
|
||||
$success = "Permisos de Discord verificados para el servidor: " . htmlspecialchars($permissionsInfo['guild_name']);
|
||||
} else {
|
||||
$error = "No se pudo obtener la información de roles del bot en el servidor de Discord.";
|
||||
}
|
||||
} else {
|
||||
$error = "No se pudieron obtener los roles del servidor de Discord. Código HTTP: $httpCode";
|
||||
}
|
||||
} else {
|
||||
$error = "Error al obtener información del servidor de Discord. Código HTTP: $httpCode";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al verificar permisos de Discord: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
// Verificar si es una solicitud de verificación de webhook de Telegram
|
||||
elseif (isset($_POST['check_telegram_webhook'])) {
|
||||
try {
|
||||
if (!defined('TELEGRAM_BOT_TOKEN') || empty(TELEGRAM_BOT_TOKEN)) {
|
||||
throw new Exception("La constante TELEGRAM_BOT_TOKEN no está definida o está vacía.");
|
||||
}
|
||||
|
||||
$botToken = TELEGRAM_BOT_TOKEN;
|
||||
$apiUrl = "https://api.telegram.org/bot{$botToken}/getWebhookInfo";
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 10, // Segundos para esperar la conexión
|
||||
CURLOPT_TIMEOUT => 20, // Segundos para la ejecución total de cURL
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception('Error en cURL al contactar la API de Telegram: ' . curl_error($ch));
|
||||
}
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
throw new Exception("La API de Telegram devolvió un código HTTP {$httpCode}. Respuesta: " . $response);
|
||||
}
|
||||
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if (!$result['ok']) {
|
||||
throw new Exception("Error de la API de Telegram: " . ($result['description'] ?? 'Error desconocido'));
|
||||
}
|
||||
|
||||
$telegramWebhookInfo = $result['result'];
|
||||
$success = "Información del webhook de Telegram obtenida correctamente.";
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al verificar el webhook de Telegram: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
// Manejar la eliminación del webhook de Telegram
|
||||
elseif (isset($_POST['delete_telegram_webhook'])) {
|
||||
try {
|
||||
if (!defined('TELEGRAM_BOT_TOKEN') || empty(TELEGRAM_BOT_TOKEN)) {
|
||||
throw new Exception("La constante TELEGRAM_BOT_TOKEN no está definida o está vacía.");
|
||||
}
|
||||
$botToken = TELEGRAM_BOT_TOKEN;
|
||||
$apiUrl = "https://api.telegram.org/bot{$botToken}/deleteWebhook";
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if ($httpCode === 200 && $result['ok']) {
|
||||
$success = "Webhook de Telegram eliminado correctamente.";
|
||||
} else {
|
||||
throw new Exception("Error al eliminar el webhook: " . ($result['description'] ?? 'Respuesta inválida de la API.'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al eliminar el webhook de Telegram: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
// Manejar la configuración del webhook de Telegram
|
||||
elseif (isset($_POST['set_telegram_webhook'])) {
|
||||
try {
|
||||
// Las constantes son definidas en config.php
|
||||
if (!defined('TELEGRAM_BOT_TOKEN') || !defined('BOT_BASE_URL') || !defined('TELEGRAM_WEBHOOK_TOKEN')) {
|
||||
throw new Exception("Una o más constantes requeridas (TELEGRAM_BOT_TOKEN, BOT_BASE_URL, TELEGRAM_WEBHOOK_TOKEN) no están definidas. Revisa tu archivo .env y la configuración.");
|
||||
}
|
||||
|
||||
$botToken = TELEGRAM_BOT_TOKEN;
|
||||
$webhookUrl = rtrim(BOT_BASE_URL, '/') . '/telegram/webhook/telegram_bot_webhook.php?auth_token=' . TELEGRAM_WEBHOOK_TOKEN;
|
||||
$apiUrl = "https://api.telegram.org/bot{$botToken}/setWebhook?url=" . urlencode($webhookUrl);
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if ($httpCode === 200 && $result['ok']) {
|
||||
$success = "Webhook de Telegram configurado correctamente en: " . htmlspecialchars($webhookUrl);
|
||||
} else {
|
||||
throw new Exception("Error al configurar el webhook: " . ($result['description'] ?? 'Respuesta inválida de la API.'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al configurar el webhook de Telegram: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
// Si es un envío de mensaje de prueba de Discord
|
||||
elseif (isset($_POST['discord_id']) && !empty($_POST['test_content'])) {
|
||||
$recipientId = trim($_POST['discord_id']);
|
||||
$testContent = trim($_POST['test_content']);
|
||||
$testRecipientType = $_POST['test_recipient_type'] ?? 'channel';
|
||||
|
||||
try {
|
||||
$logDir = __DIR__ . '/../logs';
|
||||
if (!is_dir($logDir)) mkdir($logDir, 0755, true);
|
||||
error_log("Test Discord: ID={$recipientId}, Type={$testRecipientType}, Token=" . substr(DISCORD_BOT_TOKEN, 0, 8) . "...", 3, $logDir . '/discord_api.log');
|
||||
|
||||
$discordSender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
$discordResponse = $discordSender->sendMessage($recipientId, $testContent, $testRecipientType);
|
||||
$success = "Mensaje de prueba de Discord enviado con éxito.";
|
||||
} catch (Exception $e) {
|
||||
$error = "Error al enviar mensaje de prueba de Discord: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "Por favor, completa todos los campos requeridos.";
|
||||
}
|
||||
}
|
||||
|
||||
// Incluir el encabezado que contiene el menú lateral
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
?>
|
||||
|
||||
<!-- Contenido principal -->
|
||||
<div id="page-content-wrapper">
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Probar Conexiones de Bots</h1>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Enviar Mensaje de Prueba a Discord</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="discord_id" class="form-label">ID de Canal o Usuario de Discord</label>
|
||||
<input type="text" class="form-control" id="discord_id" name="discord_id" required
|
||||
value="<?php echo htmlspecialchars($_POST['discord_id'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tipo de Destinatario</label>
|
||||
<div>
|
||||
<input type="radio" id="testRecipientTypeChannel" name="test_recipient_type" value="channel"
|
||||
<?php echo (($_POST['test_recipient_type'] ?? 'channel') === 'channel') ? 'checked' : ''; ?>>
|
||||
<label for="testRecipientTypeChannel">Canal</label>
|
||||
|
||||
<input type="radio" id="testRecipientTypeUser" name="test_recipient_type" value="user" class="ms-3"
|
||||
<?php echo (($_POST['test_recipient_type'] ?? 'channel') === 'user') ? 'checked' : ''; ?>>
|
||||
<label for="testRecipientTypeUser">Usuario</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="test_content" class="form-label">Contenido del Mensaje</label>
|
||||
<textarea class="form-control" id="test_content" name="test_content" rows="3" required><?php
|
||||
echo htmlspecialchars($_POST['test_content'] ?? 'Mensaje de prueba desde el bot.');
|
||||
?></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Enviar Prueba a Discord</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($discordResponse) && $discordResponse !== null): ?>
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header"><h5 class="mb-0">Respuesta de la API de Discord</h5></div>
|
||||
<div class="card-body"><pre><code><?php print_r($discordResponse); ?></code></pre></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header"><h5 class="mb-0">Verificar Permisos del Bot de Discord</h5></div>
|
||||
<div class="card-body">
|
||||
<form action="" method="POST" class="mb-4">
|
||||
<input type="hidden" name="check_permissions" value="1">
|
||||
<div class="mb-3">
|
||||
<label for="guild_id" class="form-label">ID del Servidor de Discord</label>
|
||||
<input type="text" class="form-control" id="guild_id" name="guild_id" required
|
||||
value="<?php echo htmlspecialchars($_POST['guild_id'] ?? ''); ?>"
|
||||
placeholder="Ej: 123456789012345678">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-info">Verificar Permisos de Discord</button>
|
||||
</form>
|
||||
|
||||
<?php if (!empty($permissionsInfo)): ?>
|
||||
<div class="alert alert-info">
|
||||
<h6>Resultado de la verificación de Discord:</h6>
|
||||
<ul class="mb-0">
|
||||
<li>Servidor: <strong><?php echo htmlspecialchars($permissionsInfo['guild_name']); ?></strong></li>
|
||||
<li>Es Administrador:
|
||||
<span class="badge bg-<?php echo $permissionsInfo['bot_has_admin'] ? 'success' : 'danger'; ?>">
|
||||
<?php echo $permissionsInfo['bot_has_admin'] ? 'Sí' : 'No'; ?>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header"><h5 class="mb-0">Verificar Estado del Webhook de Telegram</h5></div>
|
||||
<div class="card-body">
|
||||
<p>Obtén el estado actual de tu webhook directamente desde la API de Telegram para diagnosticar problemas de conexión.</p>
|
||||
<form action="" method="POST">
|
||||
<input type="hidden" name="check_telegram_webhook" value="1">
|
||||
<button type="submit" class="btn btn-info">Verificar Webhook de Telegram</button>
|
||||
</form>
|
||||
|
||||
<?php if (isset($telegramWebhookInfo) && is_array($telegramWebhookInfo)): ?>
|
||||
<div class="mt-4">
|
||||
<h6>Resultado de la verificación de Telegram:</h6>
|
||||
<?php if (empty($telegramWebhookInfo['url'])): ?>
|
||||
<div class="alert alert-danger">
|
||||
<strong>No hay webhook configurado.</strong> El bot está funcionando en modo 'getUpdates'.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"><strong>URL:</strong> <code><?php echo htmlspecialchars($telegramWebhookInfo['url']); ?></code></li>
|
||||
<li class="list-group-item"><strong>Actualizaciones pendientes:</strong>
|
||||
<span class="badge bg-<?php echo $telegramWebhookInfo['pending_update_count'] > 0 ? 'warning' : 'success'; ?>">
|
||||
<?php echo $telegramWebhookInfo['pending_update_count']; ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php if (!empty($telegramWebhookInfo['last_error_date'])): ?>
|
||||
<li class="list-group-item list-group-item-danger">
|
||||
<strong>Último error:</strong> <?php echo date('Y-m-d H:i:s', $telegramWebhookInfo['last_error_date']); ?>
|
||||
<br>
|
||||
<strong>Mensaje:</strong> <?php echo htmlspecialchars($telegramWebhookInfo['last_error_message']); ?>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li class="list-group-item list-group-item-success"><strong>Último error:</strong> Ninguno reportado.</li>
|
||||
<?php endif; ?>
|
||||
<li class="list-group-item"><strong>Máx. Conexiones:</strong> <?php echo $telegramWebhookInfo['max_connections'] ?? 'No definido'; ?></li>
|
||||
<li class="list-group-item"><strong>Actualizaciones permitidas:</strong>
|
||||
<?php if (!empty($telegramWebhookInfo['allowed_updates']) && count($telegramWebhookInfo['allowed_updates']) > 0): ?>
|
||||
<code><?php echo implode(', ', $telegramWebhookInfo['allowed_updates']); ?></code>
|
||||
<?php else: ?>
|
||||
<span>Todos los tipos (por defecto).</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header"><h5 class="mb-0">Gestionar Webhook de Telegram</h5></div>
|
||||
<div class="card-body">
|
||||
<p>Usa estos botones para eliminar o re-configurar el webhook de Telegram. Esto es útil para forzar a Telegram a actualizar la dirección IP de tu servidor si ha cambiado (por ejemplo, con DuckDNS).</p>
|
||||
<form action="" method="POST" class="d-inline me-2">
|
||||
<input type="hidden" name="delete_telegram_webhook" value="1">
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('¿Estás seguro de que quieres eliminar el webhook? El bot dejará de recibir actualizaciones.');">Eliminar Webhook</button>
|
||||
</form>
|
||||
<form action="" method="POST" class="d-inline">
|
||||
<input type="hidden" name="set_telegram_webhook" value="1">
|
||||
<button type="submit" class="btn btn-success">Configurar Webhook</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts de Bootstrap -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
// Toggle del menú lateral
|
||||
function toggleSidebar() {
|
||||
document.getElementById('wrapper').classList.toggle('toggled');
|
||||
}
|
||||
|
||||
// Función para copiar al portapapeles
|
||||
function copyToClipboard(elementId) {
|
||||
var copyText = document.getElementById(elementId);
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
document.execCommand("copy");
|
||||
|
||||
var button = event.target;
|
||||
var originalText = button.innerHTML;
|
||||
button.innerHTML = '¡Copiado!';
|
||||
button.classList.remove('btn-outline-secondary');
|
||||
button.classList.add('btn-success');
|
||||
|
||||
setTimeout(function() {
|
||||
button.innerHTML = originalText;
|
||||
button.classList.remove('btn-success');
|
||||
button.classList.add('btn-outline-secondary');
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
42
admin/update_language_flag.php
Executable file
42
admin/update_language_flag.php
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// admin/update_language_flag.php
|
||||
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Solo para administradores
|
||||
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||
echo json_encode(['success' => false, 'error' => 'Acceso denegado.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que la solicitud sea AJAX y POST
|
||||
if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') !== 'xmlhttprequest' || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Solicitud no válida.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$langId = $data['id'] ?? null;
|
||||
$flagEmoji = $data['flag_emoji'] ?? '';
|
||||
|
||||
if ($langId === null) {
|
||||
echo json_encode(['success' => false, 'error' => 'ID de idioma no proporcionado.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE supported_languages SET flag_emoji = ? WHERE id = ?");
|
||||
$stmt->execute([$flagEmoji, $langId]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error en update_language_flag.php: " . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'error' => 'Error en la base de datos.']);
|
||||
}
|
||||
?>
|
||||
49
admin/update_language_status.php
Executable file
49
admin/update_language_status.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// admin/update_language_status.php
|
||||
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Verificar que la solicitud sea AJAX y POST
|
||||
if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') !== 'xmlhttprequest' || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Solicitud no válida.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Solo para administradores
|
||||
if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
|
||||
echo json_encode(['success' => false, 'error' => 'Acceso denegado.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Obtener datos del cuerpo de la solicitud
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$langId = $data['id'] ?? null;
|
||||
$isActive = isset($data['is_active']) ? (int)$data['is_active'] : null;
|
||||
|
||||
if ($langId === null || $isActive === null) {
|
||||
echo json_encode(['success' => false, 'error' => 'Datos incompletos.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE supported_languages SET is_active = ? WHERE id = ?");
|
||||
$stmt->execute([$isActive, $langId]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
// No se actualizó ninguna fila, podría ser que el ID no exista
|
||||
echo json_encode(['success' => false, 'error' => 'El idioma no fue encontrado o el estado ya era el mismo.']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Loguear el error real en el servidor
|
||||
error_log("Error en update_language_status.php: " . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'error' => 'Error en la base de datos.']);
|
||||
}
|
||||
?>
|
||||
201
admin/users.php
Executable file
201
admin/users.php
Executable file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/session_check.php';
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/activity_logger.php';
|
||||
|
||||
// Admin-only access
|
||||
if ($_SESSION['role'] !== 'admin') {
|
||||
header('Location: ../index.php?error=unauthorized');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Inicializar variables para el modo edición
|
||||
$edit_mode = false;
|
||||
$edit_id = null;
|
||||
$edit_username = '';
|
||||
$edit_role = 'user';
|
||||
$edit_telegram_chat_id = '';
|
||||
|
||||
// Handle GET actions for editing
|
||||
if (isset($_GET['edit'])) {
|
||||
$edit_id = $_GET['edit'];
|
||||
$stmt = $pdo->prepare("SELECT id, username, role, telegram_chat_id FROM users WHERE id = ?");
|
||||
$stmt->execute([$edit_id]);
|
||||
$user_to_edit = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user_to_edit) {
|
||||
$edit_mode = true;
|
||||
$edit_username = $user_to_edit['username'];
|
||||
$edit_role = $user_to_edit['role'];
|
||||
$edit_telegram_chat_id = $user_to_edit['telegram_chat_id'];
|
||||
} else {
|
||||
$error = "Usuario no encontrado.";
|
||||
}
|
||||
}
|
||||
|
||||
// Handle POST actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Action: Create or Update User
|
||||
if (isset($_POST['save_user'])) {
|
||||
$username = $_POST['username'];
|
||||
$role = $_POST['role'];
|
||||
$telegram_chat_id = trim($_POST['telegram_chat_id']);
|
||||
$is_edit = isset($_POST['edit_id']);
|
||||
|
||||
if (empty($username) || empty($role)) {
|
||||
$error = "El nombre de usuario y el rol son obligatorios.";
|
||||
} elseif (!empty($telegram_chat_id) && !is_numeric($telegram_chat_id)) {
|
||||
$error = "El ID de Chat de Telegram debe ser un número.";
|
||||
} else {
|
||||
$chat_id_to_save = empty($telegram_chat_id) ? null : $telegram_chat_id;
|
||||
try {
|
||||
if ($is_edit) {
|
||||
$edit_id = $_POST['edit_id'];
|
||||
$details = 'Admin ' . $_SESSION['username'] . ' updated user: ' . $username . ' (ID: ' . $edit_id . ').';
|
||||
|
||||
if (!empty($_POST['password'])) {
|
||||
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare("UPDATE users SET username = ?, password = ?, role = ?, telegram_chat_id = ? WHERE id = ?");
|
||||
$stmt->execute([$username, $hashedPassword, $role, $chat_id_to_save, $edit_id]);
|
||||
$details .= ' Password was changed.';
|
||||
} else {
|
||||
$stmt = $pdo->prepare("UPDATE users SET username = ?, role = ?, telegram_chat_id = ? WHERE id = ?");
|
||||
$stmt->execute([$username, $role, $chat_id_to_save, $edit_id]);
|
||||
}
|
||||
log_activity($_SESSION['user_id'], 'User Updated', $details);
|
||||
header('Location: users.php?success=updated');
|
||||
exit();
|
||||
} else {
|
||||
if (empty($_POST['password'])) {
|
||||
$error = "La contraseña es obligatoria para nuevos usuarios.";
|
||||
} else {
|
||||
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password, role, telegram_chat_id) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$username, $hashedPassword, $role, $chat_id_to_save]);
|
||||
$new_user_id = $pdo->lastInsertId();
|
||||
log_activity($_SESSION['user_id'], 'User Created', 'Admin ' . $_SESSION['username'] . ' created new user: ' . $username . ' (ID: ' . $new_user_id . ')');
|
||||
header('Location: users.php?success=created');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error = ($e->errorInfo[1] == 1062) ? "El nombre de usuario ya existe." : "Error al guardar el usuario: " . $e->getMessage();
|
||||
if ($is_edit) {
|
||||
$edit_mode = true;
|
||||
$edit_id = $_POST['edit_id'];
|
||||
$edit_username = $username;
|
||||
$edit_role = $role;
|
||||
$edit_telegram_chat_id = $telegram_chat_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ... (Otras acciones POST como eliminar, etc. se mantienen aquí)
|
||||
}
|
||||
|
||||
// Fetch all users to display
|
||||
$users = $pdo->query("SELECT id, username, role, created_at, telegram_chat_id FROM users ORDER BY username ASC")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
require_once __DIR__ . '/../templates/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4" data-translate="true">Administrar Usuarios</h1>
|
||||
|
||||
<?php if (isset($error)): ?><div class="alert alert-danger"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||||
<?php if (isset($_GET['success'])): /* ... Lógica de mensajes de éxito ... */ endif; ?>
|
||||
|
||||
<!-- Create/Edit User Form -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0" data-translate="true"><?= $edit_mode ? 'Editar Usuario' : 'Crear Nuevo Usuario' ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="users.php" method="POST">
|
||||
<?php if ($edit_mode): ?>
|
||||
<input type="hidden" name="edit_id" value="<?= $edit_id ?>">
|
||||
<?php endif; ?>
|
||||
<div class="row align-items-end">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="username" class="form-label" data-translate="true">Usuario</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
value="<?= htmlspecialchars($edit_username) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="password" class="form-label">
|
||||
<span data-translate="true">Contraseña</span> <?php if ($edit_mode): ?><small class="text-muted" data-translate="true">(no cambiar)</small><?php endif; ?>
|
||||
</label>
|
||||
<input type="password" class="form-control" id="password" name="password" <?= !$edit_mode ? 'required' : '' ?>>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label for="role" class="form-label" data-translate="true">Rol</label>
|
||||
<select class="form-select" id="role" name="role" required>
|
||||
<option value="user" <?= ($edit_role === 'user') ? 'selected' : '' ?> data-translate="true">Usuario</option>
|
||||
<option value="admin" <?= ($edit_role === 'admin') ? 'selected' : '' ?> data-translate="true">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<label for="telegram_chat_id" class="form-label" data-translate="true">ID Chat Telegram</label>
|
||||
<input type="text" class="form-control" id="telegram_chat_id" name="telegram_chat_id"
|
||||
value="<?= htmlspecialchars($edit_telegram_chat_id) ?>" placeholder="(Opcional)">
|
||||
</div>
|
||||
<div class="col-md-2 mb-3">
|
||||
<button type="submit" name="save_user" class="btn btn-<?= $edit_mode ? 'success' : 'primary' ?> w-100" data-translate="true">
|
||||
<?= $edit_mode ? 'Actualizar' : 'Crear' ?>
|
||||
</button>
|
||||
<?php if ($edit_mode): ?>
|
||||
<a href="users.php" class="btn btn-outline-secondary w-100 mt-2" data-translate="true">Cancelar</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Users List -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header"><h5 data-translate="true">Lista de Usuarios</h5></div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th data-translate="true">ID</th>
|
||||
<th data-translate="true">Usuario</th>
|
||||
<th data-translate="true">Rol</th>
|
||||
<th data-translate="true">ID Chat Telegram</th>
|
||||
<th data-translate="true">Creado en</th>
|
||||
<th class="text-center" data-translate="true">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?= $user['id'] ?></td>
|
||||
<td><?= htmlspecialchars($user['username']) ?></td>
|
||||
<td><?= ucfirst($user['role']) ?></td>
|
||||
<td>
|
||||
<?php if (!empty($user['telegram_chat_id'])): ?>
|
||||
<span class="badge bg-info text-dark"><?= htmlspecialchars($user['telegram_chat_id']) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary" data-translate="true">No vinculado</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= date('d/m/Y H:i', strtotime($user['created_at'])) ?></td>
|
||||
<td class="text-center">
|
||||
<a href="?edit=<?= $user['id'] ?>#username" class="btn btn-sm btn-primary" title="Editar" data-translate-title="true">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<!-- Otros botones de acción (modal, eliminar) -->
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modales y Scripts -->
|
||||
<?php require_once __DIR__ . '/../templates/footer.php'; ?>
|
||||
Reference in New Issue
Block a user