From c2f12ff64fd13c6426d16301c9cbf84feaf486bf Mon Sep 17 00:00:00 2001 From: SBen-IV Date: Mon, 1 May 2023 16:44:22 -0300 Subject: [PATCH] feat: ahora se guarda el estado de noticias_automaticas y si estaba prendido, al revivir el bot se setean automaticamente; otros cambios menores --- .gitignore | 2 +- connectors/fiuba_web.py | 5 +-- connectors/silk.py | 11 +++--- controllers/jjjameson.py | 41 ++++++++++++++++------- exceptions/cantidad_noticias_exception.py | 7 ++-- main.py | 9 +++-- repositories/estado_repository.py | 30 +++++++++++++++++ repositories/noticias_repository.py | 11 +++--- view/threats_and_menaces.py | 1 + 9 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 repositories/estado_repository.py diff --git a/.gitignore b/.gitignore index 379ee55..598a033 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ -noticias.json \ No newline at end of file +*.json \ No newline at end of file diff --git a/connectors/fiuba_web.py b/connectors/fiuba_web.py index 1c12e89..7810b67 100644 --- a/connectors/fiuba_web.py +++ b/connectors/fiuba_web.py @@ -2,12 +2,13 @@ from entities.noticia import Noticia + class FiubaWeb(ABC): - + @abstractclassmethod def obtener_noticias(self, n: int = 1) -> list: pass @abstractclassmethod def obtener_noticias_nuevas(self, ultima_noticia: Noticia) -> list: - pass \ No newline at end of file + pass diff --git a/connectors/silk.py b/connectors/silk.py index 4753f7f..aff53f2 100644 --- a/connectors/silk.py +++ b/connectors/silk.py @@ -16,6 +16,7 @@ INICIO_TITULO = 8 MAX_NOTICIAS = 16 + class Silk(FiubaWeb): def __init__(self): self.logger = logging.getLogger(__class__.__name__) @@ -31,7 +32,7 @@ def obtener_noticias(self, n_noticias: int = 1) -> list: noticias.append(self.obtener_noticia(uri)) return noticias - + def obtener_noticia(self, uri: str) -> Noticia: url = DOMINIO + uri self.logger.info("Obteniendo noticia de {url}".format(url=url)) @@ -50,7 +51,7 @@ def obtener_noticia(self, uri: str) -> Noticia: titulo = soup.title.get_text()[INICIO_TITULO:] return Noticia(titulo, descripcion, fecha, url) - + def obtener_noticias_nuevas(self, ultima_noticia: Noticia) -> list: uris_noticias = self.__obtener_uri_noticias() @@ -58,20 +59,20 @@ def obtener_noticias_nuevas(self, ultima_noticia: Noticia) -> list: for uri in uris_noticias[:MAX_NOTICIAS]: noticia = self.obtener_noticia(uri) - + if noticia.fecha > ultima_noticia.fecha: noticias_nuevas.append(noticia) else: break return noticias_nuevas - + def __obtener_uri_noticias(self) -> list: page = requests.get(LINK_NOTICIAS) soup = BeautifulSoup(page.content, 'html.parser') return list(map(lambda x: x.get('href'), soup.select(".noticia > a"))) - + def __validar_cantidad(self, n_noticias: int) -> None: if n_noticias <= 0: raise CantidadNoticiasNegativaException(n_noticias) diff --git a/controllers/jjjameson.py b/controllers/jjjameson.py index 07dc66e..a2a9849 100644 --- a/controllers/jjjameson.py +++ b/controllers/jjjameson.py @@ -5,20 +5,28 @@ from connectors.fiuba_web import FiubaWeb from view.imprenta import Imprenta from repositories.noticias_repository import NoticiasRepository +from repositories.estado_repository import EstadoRepository from error_handler import logging from exceptions.cantidad_noticias_exception import CantidadNoticiasNoEsNumeroException ID_CANAL_NOTICIAS = os.getenv('ID_CANAL_NOTICIAS') -INTERVALO_MENSAJES_AUTOMATICOS = 3*60*60 # En segundos +INTERVALO_MENSAJES_AUTOMATICOS = 3*60*60 # En segundos + class JJJameson: - def __init__(self, fiuba_web: FiubaWeb, repo: NoticiasRepository, imprenta: Imprenta): + def __init__(self, fiuba_web: FiubaWeb, noticias_repo: NoticiasRepository, estado_repo: EstadoRepository, imprenta: Imprenta, job_queue, bot): self.fiuba_web = fiuba_web - self.repo = repo + self.repo = noticias_repo + self.estado_repo = estado_repo self.imprenta = imprenta - self.noticias_automaticas = False - self.job = None + self.noticias_automaticas = self.estado_repo.noticias_automaticas() + + if self.noticias_automaticas: + self.__activar_noticias_automaticas(job_queue, bot.get_chat(ID_CANAL_NOTICIAS)) + else: + self.job = None + self.logger = logging.getLogger(__class__.__name__) def conseguir_noticias(self, update: Update, context: CallbackContext): @@ -39,29 +47,33 @@ def conseguir_noticias(self, update: Update, context: CallbackContext): self.logger.warn("Cantidad de noticias no es número {arg}".format(arg=context.args[0])) raise CantidadNoticiasNoEsNumeroException(arg=context.args[0]) + def __activar_noticias_automaticas(self, job_queue, chat): + self.job = job_queue.run_repeating(self.conseguir_noticias_automatico, INTERVALO_MENSAJES_AUTOMATICOS, context=chat) + def activar_noticias_automaticas(self, update: Update, context: CallbackContext): - if self.noticias_automaticas == False: - self.job = context.job_queue.run_repeating(self.conseguir_noticias_automatico, INTERVALO_MENSAJES_AUTOMATICOS, context=context.bot.get_chat(ID_CANAL_NOTICIAS)) + if not self.noticias_automaticas: + self.__activar_noticias_automaticas(context.job_queue, context.bot.get_chat(ID_CANAL_NOTICIAS)) self.noticias_automaticas = True - self.logger.info("Se activaron las noticias automaticas.") + self.estado_repo.guardar(self.noticias_automaticas) + self.logger.info("Se activaron las noticias automáticas.") update.effective_chat.send_message("Se activaron las noticias automáticas.") else: - update.effective_chat.send_message("Las noticias automáticas ya estan activadas.") - + update.effective_chat.send_message("Las noticias automáticas ya están activadas.") def desactivar_noticias_automaticas(self, update: Update, _: CallbackContext): - if self.noticias_automaticas == True: + if self.noticias_automaticas: self.job.schedule_removal() self.noticias_automaticas = False + self.estado_repo.guardar(self.noticias_automaticas) self.logger.info("Se desactivaron las noticias automáticas.") update.effective_chat.send_message("Se desactivaron las noticias automáticas.") else: - update.effective_chat.send_message("Las noticias automáticas no estan activadas.") + update.effective_chat.send_message("Las noticias automáticas no están activadas.") def conseguir_noticias_automatico(self, context: CallbackContext): ultima_noticia_guardada = self.repo.ultima_noticia() self.logger.info("Fecha de última noticia {titulo} es {fecha}.".format(titulo=ultima_noticia_guardada.titulo, fecha=ultima_noticia_guardada.fecha)) - + nuevas_noticias = self.fiuba_web.obtener_noticias_nuevas(ultima_noticia_guardada) if len(nuevas_noticias) > 0: @@ -70,3 +82,6 @@ def conseguir_noticias_automatico(self, context: CallbackContext): self.imprenta.enviar_noticias(context.job.context, nuevas_noticias, 30) else: self.logger.info("No hay noticias nuevas.") + + def estado(self, update: Update, _: CallbackContext): + update.effective_chat.send_message("Noticias automáticas: {noticias_automaticas}".format(noticias_automaticas=self.noticias_automaticas)) diff --git a/exceptions/cantidad_noticias_exception.py b/exceptions/cantidad_noticias_exception.py index 89f69cf..9314360 100644 --- a/exceptions/cantidad_noticias_exception.py +++ b/exceptions/cantidad_noticias_exception.py @@ -3,16 +3,19 @@ def __init__(self, message) -> None: self.message = message super().__init__(self.message) + class CantidadNoticiasNoEsNumeroException(CantidadNoticiasException): - def __init__(self, arg = "") -> None: + def __init__(self, arg="") -> None: self.message = "No creo que '{arg}' sea un número.".format(arg=arg) super().__init__(self.message) - + + class CantidadNoticiasNegativaException(CantidadNoticiasException): def __init__(self, arg) -> None: self.message = "¿Cómo puedo conseguir {arg} noticias?".format(arg=arg) super().__init__(self.message) + class CantidadNoticiasMaximaException(CantidadNoticiasException): def __init__(self, arg) -> None: self.message = "¿Quieres que consiga {arg} noticias? Aumentame el sueldo.".format(arg=arg) diff --git a/main.py b/main.py index d29dffb..c25adf6 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,7 @@ from connectors.silk import Silk from view.threats_and_menaces import ThreatsAndMenaces from repositories.noticias_repository import NoticiasRepository +from repositories.estado_repository import EstadoRepository from error_handler import error_handler, logging from telegram.ext import Updater, Filters, CommandHandler from dotenv import load_dotenv @@ -13,6 +14,7 @@ logger = logging.getLogger('main') + def main(): updater = Updater(token=os.getenv('BOT_TOKEN'), use_context=True) @@ -20,21 +22,22 @@ def main(): logger.info("Iniciando {full_name} (@{username}).".format(full_name=bot.full_name, username=bot.username)) - jameson = JJJameson(Silk(), NoticiasRepository(), ThreatsAndMenaces()) + jameson = JJJameson(Silk(), NoticiasRepository(), EstadoRepository(), ThreatsAndMenaces(), updater.job_queue, updater.bot) filtro = Filters.chat(chat_id=int(os.getenv('ID_GRUPO_NOTICIAS'))) updater.dispatcher.add_handler(CommandHandler('noticias', jameson.conseguir_noticias, filtro)) updater.dispatcher.add_handler(CommandHandler('empezar', jameson.activar_noticias_automaticas, filtro)) updater.dispatcher.add_handler(CommandHandler('terminar', jameson.desactivar_noticias_automaticas, filtro)) + updater.dispatcher.add_handler(CommandHandler('estado', jameson.estado, filtro)) updater.dispatcher.add_error_handler(error_handler) try: updater.start_polling() logger.info("Iniciado, esperando por comandos...") - + updater.idle() except Exception as e: logger.error(e.__cause__) -main() \ No newline at end of file +main() diff --git a/repositories/estado_repository.py b/repositories/estado_repository.py new file mode 100644 index 0000000..38f2512 --- /dev/null +++ b/repositories/estado_repository.py @@ -0,0 +1,30 @@ +import os +import json + +ESTADO_DEFAULT_JSON = { + "noticias_automaticas": False +} + + +class EstadoRepository: + def __init__(self, path: str = "estado.json"): + if not os.path.exists(path): + with open(path, "w", encoding='utf-8') as f: + json.dump(ESTADO_DEFAULT_JSON, f, indent=4) + + self.path = path + + def noticias_automaticas(self) -> bool: + estado = {} + with open(self.path, 'r', encoding='utf-8') as f: + estado = json.load(f) + + return estado.get("noticias_automaticas", False) + + def guardar(self, noticias_automaticas) -> None: + estado = { + "noticias_automaticas": noticias_automaticas + } + + with open(self.path, "w", encoding='utf-8') as f: + json.dump(estado, f, indent=4) diff --git a/repositories/noticias_repository.py b/repositories/noticias_repository.py index 2bfea41..e9ce458 100644 --- a/repositories/noticias_repository.py +++ b/repositories/noticias_repository.py @@ -5,9 +5,10 @@ from entities.noticia import Noticia from error_handler import logging + class NoticiasRepository: def __init__(self, path: str = "noticias.json"): - if os.path.exists(path) == False: + if not os.path.exists(path): f = open(path, "w") f.close() self.path = path @@ -18,7 +19,7 @@ def ultima_noticia(self) -> Noticia: with open(self.path, 'r', encoding='utf-8') as f: ultima_noticia_json = json.load(f) - + return self.__json_to_object(ultima_noticia_json) def guardar(self, noticia: Noticia) -> Noticia: @@ -28,7 +29,7 @@ def guardar(self, noticia: Noticia) -> Noticia: json.dump(noticia_json, f, indent=4) self.logger.info("Guardada {noticia}.".format(noticia=noticia_json)) - + return noticia def __object_to_json(self, noticia: Noticia) -> dict: @@ -40,7 +41,7 @@ def __object_to_json(self, noticia: Noticia) -> dict: } return noticia_json - + def __json_to_object(self, noticia_json: dict) -> Noticia: fecha = datetime.strptime(noticia_json['fecha'], "%Y-%m-%d %H:%M") - return Noticia(noticia_json['titulo'], noticia_json['descripcion'], fecha, noticia_json['url']) \ No newline at end of file + return Noticia(noticia_json['titulo'], noticia_json['descripcion'], fecha, noticia_json['url']) diff --git a/view/threats_and_menaces.py b/view/threats_and_menaces.py index 07da354..38c4e13 100644 --- a/view/threats_and_menaces.py +++ b/view/threats_and_menaces.py @@ -8,6 +8,7 @@ MAS_INFORMACION = emojize(":information: Más información") FORMATO_MENSAJE = "{titulo}\n\n{descripcion}\n\n{texto_url}\n" + class ThreatsAndMenaces(Imprenta): def __init__(self): self.logger = logging.getLogger(__class__.__name__)