Muy buenas expertos.
No entiendo mucho de python, pero mediante IA hice esto para un bot de telegram donde lista libros por tirulos, año, autor, caratula....
El problema es que no consigo hacer que las busquedas por autor genere una lista oordenada de todos los autores que hay. El resto de busquedas funcionan perfectamente (Listado de libros ordenados por autor y Listado de libros por año) pero para que añada por autor termina liándola la IA de una u otra forma y deja de funcionar.
Alguien sabría del codigo a poner en este codigo completo?
Como dato decir que tanto el token como la lista los agarra de archivos externos (.env y json) aunque eso se aprecia en el código:
import os
import json
import unicodedata
import logging
import re
from telegram import Update
from telegram.ext import ApplicationBuilder, MessageHandler, filters, CommandHandler, CallbackContext
from dotenv import load_dotenv
from fuzzywuzzy import process, fuzz
# Configuración del logging
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
# Cargar el token desde el archivo .env
load_dotenv()
TOKEN = os.getenv("TOKEN")
# Ruta del archivo de libros
LIBROS_FILE = os.path.join(os.path.dirname(__file__), "libros.json")
# Función para cargar libros desde el archivo JSON
def cargar_libros():
try:
with open(LIBROS_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, dict):
raise ValueError("⚠️ El archivo JSON debe contener un diccionario con los libros.")
return data
except Exception as e:
logging.error(f"⚠️ Error al cargar libros: {e}")
return {}
# Función para limpiar el texto eliminando acentos y caracteres especiales
def limpiar_texto(texto):
texto = ''.join(c for c in unicodedata.normalize('NFD', texto) if unicodedata.category(c) != 'Mn').lower()
texto = re.sub(r"[^a-zA-Z0-9\s]", "", texto) # Elimina caracteres especiales
return texto.strip()
# Manejo de errores
async def manejar_error(update: Update, context: CallbackContext):
logging.error(f"⚠️ Ocurrió un error: {context.error}")
# 📖 Listado de libros ordenados por autor
async def listado(update: Update, context):
libros = cargar_libros()
if not libros:
await update.message.reply_text("❌ No hay libros disponibles.", parse_mode="Markdown")
return
respuesta = f"🔎 {len(libros)} libros disponibles:\n\n"
libros_ordenados = sorted(libros.items(), key=lambda item: limpiar_texto(item[1]["autor"]))
autor_actual = None
for titulo, datos in libros_ordenados:
if datos["autor"] != autor_actual:
autor_actual = datos["autor"]
respuesta += f"\n🖊 *{autor_actual}*\n"
respuesta += f"📚 [{titulo}](t.me/{context.bot.username}?start={titulo.replace(' ', '_')}) 📅 {datos['año']}\n"
await update.message.reply_text(respuesta, parse_mode="Markdown")
# 📆 Listado de libros por año
async def epoca(update: Update, context):
libros = cargar_libros()
if not libros:
await update.message.reply_text("❌ No hay libros disponibles.", parse_mode="Markdown")
return
args = context.args
año_filtrado = args[0] if args and args[0].isdigit() else None
if año_filtrado:
libros_filtrados = {titulo: datos for titulo, datos in libros.items() if str(datos["año"]) == año_filtrado}
if not libros_filtrados:
await update.message.reply_text(f"❌ No hay libros disponibles para el año {año_filtrado}.", parse_mode="Markdown")
return
respuesta = f"🔎 {len(libros_filtrados)} libros disponibles en {año_filtrado}:\n\n"
libros_ordenados = sorted(libros_filtrados.items(), key=lambda item: item[1]["año"])
else:
respuesta = f"🔎 {len(libros)} libros disponibles:\n\n"
libros_ordenados = sorted(libros.items(), key=lambda item: item[1]["año"])
año_actual = None
for titulo, datos in libros_ordenados:
if datos["año"] != año_actual:
año_actual = datos["año"]
respuesta += f"\n📅 *{año_actual}*\n"
respuesta += f"📚 [{titulo}](t.me/{context.bot.username}?start={titulo.replace(' ', '_')}) 🖊 {datos['autor']}\n"
await update.message.reply_text(respuesta, parse_mode="Markdown")
# 🔍 Búsqueda mejorada con tolerancia a errores tipográficos
async def buscar(update: Update, context):
libros = cargar_libros()
consulta = limpiar_texto(update.message.text.strip())
comandos_a_ignorar = ["/listado", "/epoca", "/ayuda", "/start"]
for comando in comandos_a_ignorar:
consulta = consulta.replace(limpiar_texto(comando), "").strip()
if not consulta:
return
# Ajuste de búsqueda con umbral de similitud reducido a 60-65% para mayor tolerancia a errores
resultados = [r for r in process.extract(consulta, libros.keys(), limit=5, scorer=fuzz.WRatio) if r[1] >= 65]
if not resultados:
await update.message.reply_text("❌ No encontré ningún libro que coincida con tu búsqueda.")
return
respuesta = f"🔎 {len(resultados)} libros similares encontrados:\n\n"
for titulo, puntuacion in resultados:
datos = libros[titulo]
respuesta += f"📖 [{titulo}](t.me/{context.bot.username}?start={titulo.replace(' ', '_')}) 🖊 {datos['autor']} 📅 {datos['año']} (🔎 Similitud: {puntuacion}%)\n"
await update.message.reply_text(respuesta, parse_mode="Markdown")
# 🚀 Mostrar información del libro
async def start(update: Update, context):
args = context.args
if args:
titulo = " ".join(args).replace("_", " ").strip()
libros = cargar_libros()
titulo_real = next((key for key in libros.keys() if limpiar_texto(key) == limpiar_texto(titulo)), None)
if titulo_real:
link = libros[titulo_real].get("link", "❌ No hay enlace disponible.")
portada = libros[titulo_real].get("portada", None)
respuesta = f"📖 *{titulo_real}*\n🔗 {link}"
if portada and portada.startswith("http"):
await update.message.reply_photo(photo=portada, caption=respuesta, parse_mode="Markdown")
else:
await update.message.reply_text(respuesta, parse_mode="Markdown")
else:
await update.message.reply_text("❌ No encontré ese libro en la lista.", parse_mode="Markdown")
else:
await ayuda(update, context)
# 📌 Enviar mensaje de ayuda
async def ayuda(update: Update, context):
respuesta = """🛠 *Comandos disponibles:*
- 📜 /ayuda → Muestra este listado.
- 📖 /listado → Lista los libros por autor.
- 📆 /epoca → Lista los libros por año (/epoca AÑO para filtrar).
- 🔍 Escribe el título o una parte para buscar un libro."""
mensaje = await update.message.reply_text(respuesta, parse_mode="Markdown")
try:
await mensaje.pin()
except Exception as e:
logging.warning(f"⚠️ No se pudo anclar el mensaje de ayuda: {e}")
# 🔧 Configuración del bot
def main():
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("listado", listado))
app.add_handler(CommandHandler("epoca", epoca))
app.add_handler(CommandHandler("ayuda", ayuda))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, buscar))
app.add_error_handler(manejar_error)
logging.info("✅ Bot iniciado correctamente...")
app.run_polling()
if __name__ == "__main__":
main()