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, get_welcome_message from botdiscord.ui import PersistentTranslationView, ConfigView, WelcomeTranslationView from botdiscord.translate import get_reverse_mapping, load_lang_mappings load_config() intents = discord.Intents.default() intents.message_content = True intents.members = True bot = commands.Bot(command_prefix="!", intents=intents) @bot.event async def on_ready(): init_db() load_lang_mappings("discord") bot.add_view(PersistentTranslationView()) bot.add_view(WelcomeTranslationView()) 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}") @bot.event async def on_member_join(member): print(f"[WELCOME] Nuevo miembro: {member.name} ({member.id}) en guild: {member.guild.id}") if member.bot: print(f"[WELCOME] Ignorado: es bot") return try: welcome_config = get_welcome_message(member.guild.id) print(f"[WELCOME] Config obtained: {welcome_config}") if not welcome_config: print(f"[WELCOME] No hay configuración para guild {member.guild.id}") return enabled = welcome_config.get('enabled', False) if hasattr(welcome_config, 'get') else welcome_config[2] if not enabled: print(f"[WELCOME] Bienvenida deshabilitada para guild {member.guild.id}") return channel_id = welcome_config.get('channel_id') if hasattr(welcome_config, 'get') else welcome_config[0] message_template = welcome_config.get('message_content') if hasattr(welcome_config, 'get') else welcome_config[1] print(f"[WELCOME] Channel ID: {channel_id}, Template: {message_template}") if not channel_id or not message_template: return channel = member.guild.get_channel(channel_id) if not channel: print(f"[WELCOME] Canal {channel_id} no encontrado") return formatted_message = message_template.format( user_mention=member.mention, username=member.name, server_name=member.guild.name, member_count=member.guild.member_count ) text_escaped = html.escape(formatted_message) welcome_msg = await channel.send(formatted_message) print(f"[WELCOME] Mensaje enviado: {welcome_msg.id}") save_message( welcome_msg.id, member.guild.id, member.id, text_escaped, {}, 'discord' ) view = WelcomeTranslationView(member.guild.id) await welcome_msg.edit(content=formatted_message + "\n\n¿Traducir este mensaje?", view=view) print(f"[WELCOME] Vista de traducción añadida") except Exception as e: import traceback print(f"[WELCOME] Error: {e}") traceback.print_exc() 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()