import discord from botdiscord.translate import get_lang_mapping, get_flag_mapping, get_name_to_code, translate_text from botdiscord.database import get_message, save_translation, get_cached_translation class TranslationView(discord.ui.View): def __init__(self, text: str, languages: list, original_message, attachments=None, mentions_map=None): super().__init__(timeout=None) self.text = text self.original_message = original_message self.attachments = attachments or [] self.mentions_map = mentions_map or {} flag_mapping = get_flag_mapping() name_to_code = get_name_to_code() for lang in languages: lang_code = name_to_code.get(lang) flag = flag_mapping.get(lang_code, "") if lang_code else "" self.add_item(TranslationButton(lang, lang_code, flag, text, original_message, self.attachments, self.mentions_map)) class TranslationButton(discord.ui.Button): def __init__(self, lang_name: str, lang_code: str, flag: str, text: str, original_message, attachments=None, mentions_map=None): # Ahora el label es solo la bandera, si no hay bandera mostramos el nombre label = flag if flag else lang_name super().__init__(label=label, style=discord.ButtonStyle.primary) self.lang_name = lang_name self.lang_code = lang_code self.flag = flag self.text = text self.original_message = original_message self.attachments = attachments or [] self.mentions_map = mentions_map or {} async def callback(self, interaction: discord.Interaction): # Intentamos recuperar el mensaje de la base de datos si falta contexto msg_context = None if not self.text or not self.mentions_map: # Recuperamos de la BD usando el ID del mensaje al que se responde # En Discord, si es un reply, el original_message es el mensaje original msg_id = self.original_message.id db_msg = get_message(msg_id) if db_msg: self.text = db_msg['content'] self.mentions_map = db_msg['mentions_map'] # Verificamos si ya tenemos la traducción en caché cached = get_cached_translation(self.original_message.id, self.lang_code) if cached: translated = cached else: # Traducimos el texto translated = await translate_text(self.text, self.lang_code) # Guardamos en caché save_translation(self.original_message.id, self.lang_code, translated) # Desescapamos el HTML para recuperar caracteres especiales import html import re translated = html.unescape(translated) # Reemplazamos menciones si existen for placeholder, mention in self.mentions_map.items(): # Extraer el número del tag, ej: m0 de match = re.search(r'm\d+', placeholder) if not match: continue tag_num = match.group() # 1. Reemplazamos la etiqueta de apertura (o autocontenida) por la mención # Patrón: < \s* mX \s* /? \s* > open_pattern = re.compile(rf'<\s*{tag_num}\s*/?\s*>') translated = open_pattern.sub(mention, translated) # 2. Eliminamos cualquier etiqueta de cierre que el traductor haya "inventado" # Patrón: < \s* / \s* mX \s* > close_pattern = re.compile(rf'<\s*/\s*{tag_num}\s*>') translated = close_pattern.sub('', translated) # En lugar de enviar un mensaje nuevo, EDITAMOS el mensaje actual (el de los botones) if self.attachments: # Si hay archivos, los adjuntamos de nuevo al editar files = [] for attachment in self.attachments: file = await attachment.to_file() files.append(file) await interaction.response.edit_message(content=translated, attachments=files, view=self.view) else: # Editamos el texto "¿Traducir este mensaje?" por la traducción directamente await interaction.response.edit_message(content=translated, view=self.view) class ConfigSelect(discord.ui.Select): def __init__(self, guild_id: int, bot_type: str = "discord"): from botdiscord.database import get_active_languages lang_mapping = get_lang_mapping(bot_type) flag_mapping = get_flag_mapping(bot_type) active = get_active_languages(guild_id) options = [] for name, code in lang_mapping.items(): flag = flag_mapping.get(code, "") if flag: options.append(discord.SelectOption(label=flag, value=name, default=(code in active))) else: options.append(discord.SelectOption(label=name, value=name, default=(code in active))) super().__init__( placeholder="Selecciona los idiomas activos...", min_values=0, max_values=len(options), options=options ) async def callback(self, interaction: discord.Interaction): from botdiscord.database import set_active_languages from botdiscord.translate import get_lang_mapping, get_flag_mapping guild_id = interaction.guild_id lang_mapping = get_lang_mapping("discord") flag_mapping = get_flag_mapping("discord") selected_codes = [lang_mapping[val] for val in self.values] set_active_languages(guild_id, selected_codes) selected_flags = [] for val in self.values: code = lang_mapping.get(val) flag = flag_mapping.get(code, "") if code else "" selected_flags.append(flag if flag else val) await interaction.response.send_message( f"Configuración actualizada: {', '.join(selected_flags)}", ephemeral=True ) class ConfigView(discord.ui.View): def __init__(self, guild_id: int, bot_type: str = "discord"): super().__init__() self.add_item(ConfigSelect(guild_id, bot_type))