import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import discord from discord.ext import commands from discord import app_commands 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 from botdiscord.ui import PersistentTranslationView, ConfigView from botdiscord.translate import get_reverse_mapping, load_lang_mappings load_config() intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix="!", intents=intents) @bot.event async def on_ready(): init_db() load_lang_mappings("discord") # Registramos la vista persistente GLOBALMENTE # Esto asegura que cualquier botón con el custom_id adecuado sea escuchado bot.add_view(PersistentTranslationView()) print(f"Bot Discord conectado como {bot.user}") try: synced = await bot.tree.sync() print(f"Sincronizados {len(synced)} comandos.") except Exception as e: print(f"Error sync: {e}") def get_active_langs_for_guild(guild_id): from botdiscord.translate import get_reverse_mapping active = get_active_languages(guild_id) if not active: active = get_bot_languages("discord") if not active: active = [lang["code"] for lang in get_languages()] reverse_mapping = get_reverse_mapping("discord") # Devolvemos códigos para filtrar la vista persistente return active @bot.event async def on_message(message): if message.author.bot: return text_content = message.content.strip() if not text_content and not message.attachments: return # Filtros de stickers/emojis/etc if message.stickers or text_content.startswith('https://tenor.com/') or \ re.fullmatch(r'<(a?):[a-zA-Z0-9_]+:[0-9]+>', text_content): return if text_content and len(text_content) < 2: return active_codes = get_active_langs_for_guild(message.guild.id) if not active_codes: return # Escapar y procesar menciones text_escaped = html.escape(message.content) mention_pattern = re.compile(r'<@!?(\d+)>|<@&(\d+)>|<#(\d+)>') mentions_map = {} def replace_mention(match): placeholder = f"" mentions_map[placeholder] = html.unescape(match.group(0)) return placeholder text_to_translate = mention_pattern.sub(replace_mention, text_escaped) save_message(message.id, message.guild.id, message.author.id, text_to_translate, mentions_map, 'discord') # Creamos una vista filtrada basada en la persistente para mostrar solo los botones activos # Pero los botones mantienen sus custom_ids globales from botdiscord.ui import TranslationButton view = discord.ui.View(timeout=None) # Cargamos mapeos para etiquetas de botones from botdiscord.translate import get_name_to_code, get_flag_mapping name_to_code = get_name_to_code("discord") flag_mapping = get_flag_mapping("discord") code_to_name = {v: k for k, v in name_to_code.items()} for code in active_codes: name = code_to_name.get(code, code) flag = flag_mapping.get(code, "") view.add_item(TranslationButton(name, code, flag)) try: await message.reply("¿Traducir este mensaje?", view=view, mention_author=False) except Exception as e: print(f"Error enviando reply: {e}") @bot.tree.command(name="configurar", description="Configura los idiomas de traducción") @app_commands.checks.has_permissions(administrator=True) async def configurar(interaction: discord.Interaction): view = ConfigView(interaction.guild_id, "discord") await interaction.response.send_message("Selecciona idiomas habilitados:", view=view, ephemeral=True) def run_discord_bot(): token = get_discord_token() bot.run(token) if __name__ == "__main__": run_discord_bot()