"""Étape de récupération non bloquante des articles RSS du collège.""" from __future__ import annotations from pronote_sync.sources.blog.result import BlogRSSFetchResult from pronote_sync.sources.blog.rss import BlogRSSClient from pronote_sync.sources.blog.state import BlogRSSState from pronote_sync.utils.redaction import redact_exception def fetch_blog_step(client: BlogRSSClient | None, state: BlogRSSState | None) -> BlogRSSFetchResult: """Récupère les articles RSS nouveaux sans les acquitter. L'état des GUID est acquitté séparément par le pipeline après confirmation de la livraison XMPP. Les en-têtes de cache d'une réponse sans article peuvent être conservés immédiatement, car aucune livraison n'est alors en attente. :param client: Client RSS configuré, ou ``None`` lorsque le blog est désactivé. :param state: État de déduplication et de cache HTTP associé au run. :return: Résultat de récupération, incluant les métadonnées de cache. :rtype: BlogRSSFetchResult :raises RuntimeError: Si la récupération RSS injectée échoue. """ if client is None or state is None: return BlogRSSFetchResult() try: etag, last_modified = state.get_cache_headers() result = client.fetch_and_parse( known_guids=state.get_known_guids(), etag=etag, last_modified=last_modified ) if result.error is not None: raise RuntimeError(result.error) from None if not result.not_modified and not result.articles: state.update_cache_headers(result.etag, result.last_modified) return result except Exception as exc: raise RuntimeError(f"Récupération du blog échouée : {redact_exception(exc)}") from None