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:
2026-03-20 06:41:35 -06:00
parent 100fef5c90
commit 39f531a331
6 changed files with 743 additions and 9 deletions

View File

@@ -1,5 +1,7 @@
import os
import sys
import signal
import time
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import discord
@@ -9,7 +11,7 @@ import re
import html
from botdiscord.config import get_discord_token, load_config, get_languages
from botdiscord.database import init_db, get_active_languages, get_bot_languages, save_message, get_welcome_message
from botdiscord.database import init_db, get_active_languages, get_bot_languages, save_message, get_welcome_message, is_channel_enabled
from botdiscord.ui import PersistentTranslationView, ConfigView, WelcomeTranslationView, TranslationButton
from botdiscord.translate import get_reverse_mapping, load_lang_mappings, get_name_to_code, get_flag_mapping
@@ -115,6 +117,14 @@ def get_active_langs_for_guild(guild_id):
async def on_message(message):
if message.author.bot: return
# Verificar si el canal está habilitado para traducción
channel_enabled = is_channel_enabled(message.channel.id)
print(f"[Bot] Mensaje en canal {message.channel.id} ({message.channel.name}) - Habilitado: {channel_enabled} - Timestamp: {time.strftime('%H:%M:%S')}")
if not channel_enabled:
print(f"[Bot] Ignorando mensaje en canal desactivado: {message.channel.name}")
return
text_content = message.content.strip()
if not text_content: return

View File

@@ -190,6 +190,19 @@ def init_db():
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4''')
cursor.execute('''CREATE TABLE IF NOT EXISTS discord_servers
(server_id BIGINT NOT NULL PRIMARY KEY,
server_name VARCHAR(255) NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4''')
cursor.execute('''CREATE TABLE IF NOT EXISTS discord_channels
(channel_id BIGINT NOT NULL PRIMARY KEY,
channel_name VARCHAR(255) NOT NULL,
server_id BIGINT NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (server_id) REFERENCES discord_servers(server_id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4''')
conn.commit()
cursor.close()
else:
@@ -243,6 +256,20 @@ def init_db():
channel_id INTEGER NOT NULL,
message_content TEXT NOT NULL,
enabled INTEGER DEFAULT 1)''')
c.execute('''CREATE TABLE IF NOT EXISTS discord_servers
(server_id INTEGER PRIMARY KEY,
server_name TEXT NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
c.execute('''CREATE TABLE IF NOT EXISTS discord_channels
(channel_id INTEGER PRIMARY KEY,
channel_name TEXT NOT NULL,
server_id INTEGER NOT NULL,
is_active INTEGER DEFAULT 1,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (server_id) REFERENCES discord_servers(server_id))''')
conn.commit()
conn.close()
@@ -716,3 +743,232 @@ def delete_welcome_message(guild_id: int):
c.execute("DELETE FROM welcome_messages WHERE guild_id = ?", (guild_id,))
conn.commit()
conn.close()
# Funciones para gestión de servidores y canales de Discord
def sync_discord_servers(servers: list):
db_type = get_db_type()
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
for server in servers:
server_id = server.get('id')
server_name = server.get('name', '')
cursor.execute("""INSERT INTO discord_servers (server_id, server_name)
VALUES (%s, %s)
ON DUPLICATE KEY UPDATE server_name = %s""",
(server_id, server_name, server_name))
conn.commit()
cursor.close()
else:
conn = get_connection()
c = conn.cursor()
for server in servers:
server_id = server.get('id')
server_name = server.get('name', '')
c.execute("""INSERT OR REPLACE INTO discord_servers (server_id, server_name)
VALUES (?, ?)""", (server_id, server_name))
conn.commit()
conn.close()
def get_discord_servers() -> list:
db_type = get_db_type()
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT server_id, server_name, added_at FROM discord_servers ORDER BY server_name")
rows = cursor.fetchall()
cursor.close()
return rows
else:
conn = get_connection()
c = conn.cursor()
c.execute("SELECT server_id, server_name, added_at FROM discord_servers ORDER BY server_name")
rows = [{"server_id": r[0], "server_name": r[1], "added_at": r[2]} for r in c.fetchall()]
conn.close()
return rows
def sync_discord_channels(server_id: int, channels: list):
db_type = get_db_type()
try:
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
for channel in channels:
channel_id = channel.get('id')
channel_name = channel.get('name', '')
cursor.execute("""INSERT INTO discord_channels (channel_id, channel_name, server_id)
VALUES (%s, %s, %s)
ON DUPLICATE KEY UPDATE channel_name = %s, server_id = %s""",
(channel_id, channel_name, server_id, channel_name, server_id))
conn.commit()
cursor.close()
else:
conn = get_connection()
c = conn.cursor()
for channel in channels:
channel_id = channel.get('id')
channel_name = channel.get('name', '')
c.execute("""INSERT OR REPLACE INTO discord_channels (channel_id, channel_name, server_id)
VALUES (?, ?, ?)""", (channel_id, channel_name, server_id))
conn.commit()
conn.close()
# Limpiar todo el cache de canales después de sincronizar
clear_channel_cache()
print(f"[DB] Canales sincronizados para servidor {server_id}, cache limpiado")
except Exception as e:
print(f"[DB] Error sincronizando canales del servidor {server_id}: {e}")
raise
def get_discord_channels(server_id: int = None) -> list:
db_type = get_db_type()
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor(dictionary=True)
if server_id:
cursor.execute("SELECT channel_id, channel_name, server_id, is_active, added_at FROM discord_channels WHERE server_id = %s ORDER BY channel_name", (server_id,))
else:
cursor.execute("SELECT channel_id, channel_name, server_id, is_active, added_at FROM discord_channels ORDER BY server_id, channel_name")
rows = cursor.fetchall()
cursor.close()
return rows
else:
conn = get_connection()
c = conn.cursor()
if server_id:
c.execute("SELECT channel_id, channel_name, server_id, is_active, added_at FROM discord_channels WHERE server_id = ? ORDER BY channel_name", (server_id,))
else:
c.execute("SELECT channel_id, channel_name, server_id, is_active, added_at FROM discord_channels ORDER BY server_id, channel_name")
rows = [{"channel_id": r[0], "channel_name": r[1], "server_id": r[2], "is_active": bool(r[3]), "added_at": r[4]} for r in c.fetchall()]
conn.close()
return rows
def toggle_channel_status(channel_id: int, is_active: bool):
db_type = get_db_type()
try:
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
cursor.execute("UPDATE discord_channels SET is_active = %s WHERE channel_id = %s",
(1 if is_active else 0, channel_id))
conn.commit()
cursor.close()
else:
conn = get_connection()
c = conn.cursor()
c.execute("UPDATE discord_channels SET is_active = ? WHERE channel_id = ?",
(1 if is_active else 0, channel_id))
conn.commit()
conn.close()
# Limpiar el cache para forzar re-lectura
clear_channel_cache(channel_id)
print(f"[DB] Canal {channel_id} actualizado a {is_active}, cache limpiado")
except Exception as e:
print(f"[DB] Error actualizando canal {channel_id}: {e}")
raise
# Cache simple para estados de canales (evita consultas excesivas)
_channel_status_cache = {}
_cache_timeout = 5 # segundos
_last_cache_update = {}
def is_channel_enabled(channel_id: int) -> bool:
global _channel_status_cache, _last_cache_update
db_type = get_db_type()
current_time = time.time()
# Verificar si tenemos un cache válido
if (channel_id in _channel_status_cache and
channel_id in _last_cache_update and
current_time - _last_cache_update[channel_id] < _cache_timeout):
result = _channel_status_cache[channel_id]
print(f"[DB Cache] Canal {channel_id} estado (cache): {result}")
return result
try:
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
cursor.execute("SELECT is_active FROM discord_channels WHERE channel_id = %s", (channel_id,))
row = cursor.fetchone()
cursor.close()
print(f"[DB] MySQL Query: SELECT is_active FROM discord_channels WHERE channel_id = {channel_id}")
print(f"[DB] MySQL Row: {row}")
result = bool(row[0]) if row else False
else:
conn = get_connection()
c = conn.cursor()
c.execute("SELECT is_active FROM discord_channels WHERE channel_id = ?", (channel_id,))
row = c.fetchone()
conn.close()
print(f"[DB] SQLite Query: SELECT is_active FROM discord_channels WHERE channel_id = {channel_id}")
print(f"[DB] SQLite Row: {row}")
result = bool(row[0]) if row else False
# Actualizar cache
_channel_status_cache[channel_id] = result
_last_cache_update[channel_id] = current_time
print(f"[DB] Canal {channel_id} estado (DB): {result} - Timestamp: {time.strftime('%H:%M:%S')}")
return result
except Exception as e:
print(f"[DB] Error verificando canal {channel_id}: {e}")
# En caso de error, devolver False por seguridad
return False
def clear_channel_cache(channel_id: int = None):
"""Limpia el cache de un canal específico o de todos"""
global _channel_status_cache, _last_cache_update
if channel_id:
_channel_status_cache.pop(channel_id, None)
_last_cache_update.pop(channel_id, None)
print(f"[DB] Cache limpiado para canal {channel_id}")
# Forzar expiración inmediata poniendo un timestamp antiguo
_last_cache_update[channel_id] = 0
else:
_channel_status_cache.clear()
_last_cache_update.clear()
print(f"[DB] Todo el cache de canales limpiado")
def delete_server_channels(server_id: int):
"""Elimina todos los canales de un servidor específico"""
db_type = get_db_type()
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM discord_channels WHERE server_id = %s", (server_id,))
conn.commit()
cursor.close()
else:
conn = get_connection()
c = conn.cursor()
c.execute("DELETE FROM discord_channels WHERE server_id = ?", (server_id,))
conn.commit()
conn.close()
def delete_discord_server(server_id: int):
"""Elimina un servidor y todos sus canales"""
db_type = get_db_type()
if db_type == "mysql":
conn = get_connection()
cursor = conn.cursor()
cursor.execute("DELETE FROM discord_servers WHERE server_id = %s", (server_id,))
conn.commit()
cursor.close()
else:
conn = get_connection()
c = conn.cursor()
c.execute("DELETE FROM discord_servers WHERE server_id = ?", (server_id,))
conn.commit()
conn.close()