No description
  • Go 98.2%
  • Nix 1.8%
Find a file
Lauria 0a9a55d519 feat(search): extract item size from name + --show-url flag for visual verification
V1.4 — addresses the 'how big is it?' question for items where the
size isn't in the price column. Two complementary features:

1) Regex-based size extraction (option A, the cheap path)

  Adds items.size_text column, populated by ExtractSize() on insert
  and backfilled on migration (v2→v3) for every existing row.

  Recognised: ml, cl, l, g, kg, oz, lb — case-insensitive, case
  preserved. Handles decimals (1,5 L), ranges (33-50 G), and
  no-space forms (500ML). Returns '' when the name has no size,
  so the caller can fall back to the image.

  Covers ~17% of items directly (e.g. Maxi PC products, packaged
  goods with size in name). The remaining 83% get a verifiable
  image URL instead.

2) --show-url flag

  When set, prints the cutout_image_url (the per-product photo
  from f.wishabi.net) under each item in --flat and grouped mode,
  so the user can visually verify the size/format that wasn't
  in the name. Default off — keeps the usual output clean.

  JSON output also picks up 'size' and 'image_url' fields with
  omitempty, so scripts that want them can jq for them.

Schema migration is idempotent (ALTER TABLE ... 'duplicate column'
is treated as success) and the backfill is idempotent too
(re-running changes 0 rows on a clean DB).

Tests: 4 new test functions, 17 ExtractSize subtests, 5 render
subtests, 2 JSON subtests. All green; full ./go test passes.

Real-data smoke (J5V1A5, this week):
  $ circulaires search 'ML' --merchants Maxi --min-price 1 --flat
  shows [695 ML], [425-500 ML], [1,89 L], [320-350 G] inline.
  $ circulaires search 'sirop' --show-url prints the cutout URL
  under each SIROP D'ÉRABLE / MÉLANGE NESTLÉ match.
2026-06-18 12:13:45 -04:00
cmd/circulaires Working implementation: fetch, show, config commands 2026-06-03 09:08:04 -04:00
data Initial scaffold: Nix flake, Go module, project skeleton 2026-06-03 09:08:04 -04:00
docs docs+cli: replace real postal code with fake placeholder (H0H 0H0) 2026-06-07 13:53:15 -04:00
internal feat(search): extract item size from name + --show-url flag for visual verification 2026-06-18 12:13:45 -04:00
skills docs+cli: replace real postal code with fake placeholder (H0H 0H0) 2026-06-07 13:53:15 -04:00
.gitignore Initial scaffold: Nix flake, Go module, project skeleton 2026-06-03 09:08:04 -04:00
flake.lock Add search command for offline cache queries 2026-06-05 22:39:10 -04:00
flake.nix Initial scaffold: Nix flake, Go module, project skeleton 2026-06-03 09:08:04 -04:00
go.mod Initial scaffold: Nix flake, Go module, project skeleton 2026-06-03 09:08:04 -04:00
go.sum Accept postal codes with spaces or dashes (e.g. "J5V 1A5") 2026-06-03 09:11:29 -04:00
LICENSE Initial scaffold: Nix flake, Go module, project skeleton 2026-06-03 09:08:04 -04:00
README.md docs+cli: replace real postal code with fake placeholder (H0H 0H0) 2026-06-07 13:53:15 -04:00

circulaires

Generic CLI for fetching weekly flyers from grocery stores and retailers via the Flipp (formerly Reebee) public API.

circulaires lets you track weekly specials from any store Flipp supports, for any postal code in Canada (or ZIP in the US). It stores everything locally in SQLite so you can track price evolution over time.

Features

  • 🔎 Fetch all active flyers near a postal code
  • 🏪 Filter by merchant (IGA, Maxi, Metro, Sobeys, Walmart, …)
  • 💾 Store everything in a local SQLite database for history
  • 📊 Show current specials in a nice terminal table
  • 🇫🇷🇨🇦 French & English locale support (per postal code)
  • 🛠️ Single static binary, no runtime dependencies
  • 🧪 Pure Go SQLite (no CGO)

Quick start

# Enter the dev shell (requires Nix with flakes enabled)
nix develop

# Build
go build -o bin/circulaires ./cmd/circulaires

# Fetch this week's flyers for the North Pole, QC
./bin/circulaires fetch --postal "H0H 0H0"

# Show current specials (defaults to your saved postal code)
./bin/circulaires show

# Show only specific merchants, sorted by price
./bin/circulaires show --merchants "IGA,Maxi,Metro" --sort price-asc

Configuration

On first run, circulaires creates ~/.config/circulaires/config.yaml:

postal_code: "H0H 0H0"  # default postal code (spaces are fine — normalised before use)
locale: fr              # fr-ca | en-ca | en-us
db_path: ~/.local/share/circulaires/specials.db
merchants: []           # empty = all merchants

Override any of these via flags (--postal, --locale, etc.) or edit the file directly with circulaires config edit.

Commands

Command Description
circulaires fetch Fetch current flyers & items, store in DB
circulaires show Display current specials from DB
circulaires config View/edit config (path, show, edit, set)
circulaires version Print version info

Run circulaires <command> --help for full flag lists.

How it works

circulaires is a thin client over the unauthenticated public Flipp API that flipp.com's own web client uses. Two endpoints:

  1. GET /api/flipp/data?locale=<l>&postal_code=<p>&sid=<random> — list of all active flyers near a postal code.
  2. GET /api/flipp/flyers/<id>/flyer_items?locale=<l>&sid=<random> — items in a specific flyer.

The sid is just a random 16-digit session ID; no key, no login, no cookies. See docs/API.md for full reverse-engineering notes.

Caveats

  • The Flipp API returns every text fragment on a flyer as an "item" — product names and decorative copy ("Offers Scene + fr", "service magasin poissonnerie", "activer vos offres", …). For V1 we surface them all and let you filter via show --search. A future version will use display_type to separate real products from decorative text.
  • The same flyer can appear twice (current + next week). The DB dedupes via (flyer_id, valid_from, valid_to).
  • Locale matters: with --locale fr-ca you get "YOGOURT À BOIRE"; with --locale en-ca you get "DRINKABLE YOGURT". Pick the one your users expect.
  • This is an unofficial client. Flipp's terms apply; be polite.

Roadmap

  • Filter out decorative text items (use display_type)
  • Price comparison across merchants
  • Watchlist (notify when specific products go on sale)
  • Telegram / email digest
  • Export to CSV / JSON
  • Web UI (maybe)

Contributing

PRs welcome. Code style: gofmt + golangci-lint. Tests live next to the code they cover. Run go test ./... before pushing.

AI agent skills

This repo ships with an agent skill under skills/circulaires-cli/ for future agents (or humans) who want a quick orientation without reading Go source. The skill also documents how circulaires integrates with the mealie-cli tool for a complete "flyers → recipe → meal plan → shopping list" weekly workflow.

If you change the CLI's interface, please update the skill too. The underlying Flipp API knowledge (endpoints, sid, locale, mixed-type JSON) is in the global flipp-flyers skill — don't duplicate it here, link to it instead.

License

MIT — see LICENSE.