Sélection courante + favoris + images servables (sortie prête Hermes)

Deux outils MCP pour qu'Hermes n'ait plus de scripts à écrire :
- hf_next_delivery() : prochaine box RÉELLEMENT sélectionnée (≈4 recettes,
  pas le menu complet) + date/cutoff ; erreur stricte si introuvable
  (jamais de repli propose). Saute les semaines PAUSED via next_delivery.
- hf_favorites() : recettes favorites du compte. Champ is_favorite ajouté
  partout (hf_get_menu inclus).

Endpoints découverts (probe CDP) :
- sélection : GET /gw/my-deliveries/menu -> meals[].selection.quantity>0
- favoris   : GET /gw/cfs/v2/favorites/recipe -> items[].object_id
(GET /gw/v1/carts/{week} renvoie 404 : pas la lecture de sélection.)

Images : URLs recettes CloudFront (502) réécrites vers
img.hellofresh.com/.../hellofresh_s3/... (hellofresh/images.py),
appliqué dans Recipe.summary() -> profite à tous les outils.

README : procédure de ré-auth CDP clarifiée (refresh tokens rotatifs,
backups inutiles, page /login, profil Chrome dédié).

Outils de re-découverte : tools/probe_selection.py, tools/probe_menu_capture.py
This commit is contained in:
jerem
2026-06-18 14:18:40 +02:00
parent 4b1eb9f52c
commit 03e281a810
8 changed files with 489 additions and 10 deletions

View File

@@ -11,6 +11,8 @@ import re
from dataclasses import dataclass, field, asdict
from typing import Any
from .images import fix_image_url
_ISO_DURATION = re.compile(r"PT(?:(\d+)H)?(?:(\d+)M)?", re.IGNORECASE)
@@ -54,6 +56,7 @@ class Recipe:
contains_excluded: bool = False
matched_excludes: list[str] = field(default_factory=list)
score: float = 0.0
is_favorite: bool = False # rempli par api (best-effort) depuis le service favoris
@classmethod
def from_api(cls, raw: dict[str, Any]) -> "Recipe":
@@ -117,7 +120,7 @@ class Recipe:
"id": self.id,
"name": self.name,
"headline": self.headline,
"image_url": self.image_url,
"image_url": fix_image_url(self.image_url),
"prep_time": self.prep_time,
"prep_minutes": _iso_duration_to_minutes(self.prep_time),
"allergens": self.allergens,
@@ -125,6 +128,7 @@ class Recipe:
"matched_excludes": self.matched_excludes,
"score": self.score,
"tags": self.tags,
"is_favorite": self.is_favorite,
}