Fix Discord channel activation system
- Fix MySQL boolean conversion in toggle_channel_status - Improve cache management with 5-second timeout - Add bulk channel selection and toggle functionality - Fix Jinja2 template syntax errors - Add comprehensive debugging for channel status queries - Implement real-time channel activation without container restart
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
{% set lang = request.cookies.get('panel_lang', 'es') %}
|
||||
{% set is_admin = username == 'nickpons666' %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}">
|
||||
<head>
|
||||
@@ -48,17 +49,17 @@
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Token de Discord" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.discord.token }}" readonly>
|
||||
value="{{ config.discord.token if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Token de Telegram" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.telegram.token }}" readonly>
|
||||
value="{{ config.telegram.token if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "URL de LibreTranslate" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.libretranslate.url }}" readonly>
|
||||
value="{{ config.libretranslate.url if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,28 +108,28 @@
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Host MySQL" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.database.host }}" readonly>
|
||||
value="{{ config.database.host if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Puerto MySQL" | translate(lang) }}</label>
|
||||
<input type="number" class="form-control"
|
||||
value="{{ config.database.port }}" readonly>
|
||||
value="{{ config.database.port if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Usuario MySQL" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.database.user }}" readonly>
|
||||
value="{{ config.database.user if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Nombre de Base de Datos" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.database.name }}" readonly>
|
||||
value="{{ config.database.name if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ "Ruta de la base de datos" | translate(lang) }}</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ config.database.path }}" readonly>
|
||||
value="{{ config.database.path if is_admin else '****************************************' }}" readonly>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -77,6 +77,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-warning mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-hash"></i> {{ "Canales de Discord" | translate(lang) }}</h5>
|
||||
<p class="card-text">{{ "Administrar canales habilitados para traducción" | translate(lang) }}</p>
|
||||
<a href="/discord-channels" class="btn btn-dark btn-sm">{{ "Administrar" | translate(lang) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
|
||||
270
panel/templates/discord_channels.html
Normal file
270
panel/templates/discord_channels.html
Normal file
@@ -0,0 +1,270 @@
|
||||
{% set lang = request.cookies.get('panel_lang', 'es') %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ "Canales de Discord - Bots de Traducción" | translate(lang) }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
<style>
|
||||
.channel-item {
|
||||
border-left: 4px solid #6f42c1;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.channel-item:hover {
|
||||
border-left-color: #563d7c;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.channel-inactive {
|
||||
border-left-color: #dc3545;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.server-card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.server-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: .4s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
input:checked + .slider {
|
||||
background-color: #28a745;
|
||||
}
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/dashboard">
|
||||
<i class="bi bi-translate"></i> {{ "Bots de Traducción" | translate(lang) }}
|
||||
</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="dropdown me-3">
|
||||
<button class="btn btn-outline-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-translate"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item {{ 'active' if lang == 'es' }}" href="/set-lang/es">Español</a></li>
|
||||
<li><a class="dropdown-item {{ 'active' if lang == 'en' }}" href="/set-lang/en">English</a></li>
|
||||
<li><a class="dropdown-item {{ 'active' if lang == 'pt' }}" href="/set-lang/pt">Português</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="/dashboard" class="btn btn-outline-light btn-sm me-2">{{ "Dashboard" | translate(lang) }}</a>
|
||||
<a href="/logout" class="btn btn-outline-light btn-sm">{{ "Cerrar Sesión" | translate(lang) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>📱 {{ "Canales de Discord" | translate(lang) }}</h2>
|
||||
<div>
|
||||
<a href="/diagnosis" class="btn btn-outline-secondary me-2">
|
||||
<i class="bi bi-bug"></i> {{ "Diagnóstico" | translate(lang) }}
|
||||
</a>
|
||||
<form method="post" action="/discord-channels/sync" class="d-inline">
|
||||
<button type="submit" class="btn btn-primary" onclick="return confirm('{{ '¿Sincronizar servidores y canales desde Discord?' | translate(lang) }}')">
|
||||
<i class="bi bi-arrow-repeat"></i> {{ "Sincronizar" | translate(lang) }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if success %}
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle"></i> {{ "Configuración actualizada correctamente." | translate(lang) }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if synced %}
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle"></i> {{ "Servidores y canales sincronizados desde Discord." | translate(lang) }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if error %}
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-circle"></i> {{ "Error al realizar la operación. Verifica la configuración del bot." | translate(lang) }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if servers_with_channels|length == 0 %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> {{ "No hay servidores sincronizados. Usa el botón 'Sincronizar' para obtener los servidores y canales de Discord." | translate(lang) }}
|
||||
</div>
|
||||
{% else %}
|
||||
{% for server_id, server_data in servers_with_channels.items() %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-server"></i> {{ server_data.server_info.server_name }}
|
||||
<small class="text-muted">({{ server_data.server_info.server_id }})</small>
|
||||
</h5>
|
||||
<form method="post" action="/discord-channels/delete-server" class="d-inline">
|
||||
<input type="hidden" name="server_id" value="{{ server_id }}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"
|
||||
onclick="return confirm('{{ '¿Eliminar este servidor y todos sus canales?' | translate(lang) }}')">
|
||||
<i class="bi bi-trash"></i> {{ "Eliminar" | translate(lang) }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if server_data.channels|length == 0 %}
|
||||
<p class="text-muted">{{ "No hay canales en este servidor." | translate(lang) }}</p>
|
||||
{% else %}
|
||||
<form method="post" action="/discord-channels/bulk-toggle">
|
||||
<input type="hidden" name="server_id" value="{{ server_id }}">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50px">
|
||||
<input type="checkbox" class="form-check-input" id="selectAll_{{ server_id }}"
|
||||
onchange="toggleAllChannels({{ server_id }})">
|
||||
</th>
|
||||
<th>{{ "Canal" | translate(lang) }}</th>
|
||||
<th>{{ "ID" | translate(lang) }}</th>
|
||||
<th width="100px">{{ "Estado" | translate(lang) }}</th>
|
||||
<th width="120px">{{ "Acciones" | translate(lang) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for channel in server_data.channels %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="form-check-input channel-checkbox"
|
||||
name="channel_ids" value="{{ channel.channel_id }}"
|
||||
data-server="{{ server_id }}">
|
||||
</td>
|
||||
<td>
|
||||
<i class="bi bi-hash"></i> {{ channel.channel_name }}
|
||||
</td>
|
||||
<td><code>{{ channel.channel_id }}</code></td>
|
||||
<td>
|
||||
{% if channel.is_active %}
|
||||
<span class="badge bg-success">{{ "Activo" | translate(lang) }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ "Inactivo" | translate(lang) }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<form method="post" action="/discord-channels/toggle" class="d-inline">
|
||||
<input type="hidden" name="channel_id" value="{{ channel.channel_id }}">
|
||||
<input type="hidden" name="is_active" value="{{ 'false' if channel.is_active else 'true' }}">
|
||||
<button type="submit" class="btn {% if channel.is_active %}btn-warning{% else %}btn-success{% endif %} btn-sm"
|
||||
title="{{ 'Desactivar' if channel.is_active else 'Activar' | translate(lang) }}">
|
||||
<i class="bi bi-{% if channel.is_active %}pause{% else %}play{% endif %}"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<button type="submit" name="bulk_action" value="activate"
|
||||
class="btn btn-success btn-sm me-2" disabled>
|
||||
<i class="bi bi-play"></i> {{ "Activar seleccionados" | translate(lang) }}
|
||||
</button>
|
||||
<button type="submit" name="bulk_action" value="deactivate"
|
||||
class="btn btn-warning btn-sm" disabled>
|
||||
<i class="bi bi-pause"></i> {{ "Desactivar seleccionados" | translate(lang) }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function toggleAllChannels(serverId) {
|
||||
const selectAll = document.getElementById('selectAll_' + serverId);
|
||||
const checkboxes = document.querySelectorAll('.channel-checkbox[data-server="' + serverId + '"]');
|
||||
const bulkButtons = selectAll.closest('form').querySelectorAll('button[type="submit"][name="bulk_action"]');
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAll.checked;
|
||||
});
|
||||
|
||||
// Habilitar/deshabilitar botones bulk
|
||||
const anyChecked = document.querySelectorAll('.channel-checkbox:checked').length > 0;
|
||||
bulkButtons.forEach(btn => btn.disabled = !anyChecked);
|
||||
}
|
||||
|
||||
function updateBulkButtons() {
|
||||
const anyChecked = document.querySelectorAll('.channel-checkbox:checked').length > 0;
|
||||
document.querySelectorAll('button[type="submit"][name="bulk_action"]').forEach(btn => {
|
||||
btn.disabled = !anyChecked;
|
||||
});
|
||||
|
||||
// Actualizar checkboxes de "seleccionar todo"
|
||||
document.querySelectorAll('[id^="selectAll_"]').forEach(selectAll => {
|
||||
const serverId = selectAll.id.replace('selectAll_', '');
|
||||
const checkboxes = document.querySelectorAll('.channel-checkbox[data-server="' + serverId + '"]');
|
||||
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
|
||||
const anyChecked = Array.from(checkboxes).some(cb => cb.checked);
|
||||
|
||||
selectAll.checked = allChecked;
|
||||
selectAll.indeterminate = anyChecked && !allChecked;
|
||||
});
|
||||
}
|
||||
|
||||
// Agregar event listeners a todos los checkboxes
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.channel-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', updateBulkButtons);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user