grocery-bot

Log | Files | Refs | README

commit 638708bbc373add37a3eb5238d83a25f7346c820
Author: Chris Roberts <chris.roberts@learningunix.net>
Date:   Thu,  2 Jul 2026 14:19:30 -0500

Initial project docs: CLAUDE.md conventions and SPEC.md plan

Diffstat:
ACLAUDE.md | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASPEC.md | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+), 0 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md @@ -0,0 +1,82 @@ +# CLAUDE.md — Recipe & Grocery Telegram Bot + +## Project Purpose + +A self-hosted Telegram bot for tracking recipes, managing a weekly staples +list, and generating grocery lists. Runs as a long-polling service in an +LXC/VM on Proxmox, alongside the rest of the homelab stack. + +## Dietary Constraints (hard requirements, not preferences) + +- **Ketogenic**: recipes/ingredients should be tagged `keto` automatically + based on carb content where determinable, but always allow manual override. +- **Nut-free**: this is an allergy, not a preference. Any ingredient matching + the nut list below must hard-block the recipe from being marked nut-free, + and the bot should warn (not silently tag) when an ingredient is ambiguous + (e.g. "may contain tree nuts"). + - Nut list: almond, walnut, pecan, cashew, pistachio, hazelnut, macadamia, + brazil nut, pine nut, peanut (technically a legume but treat as a nut + here). Keep this list in a single constant, not duplicated across files. + +## Tech Stack + +- Python 3.11+ +- `python-telegram-bot` (async, v20+) — long polling, no public webhook needed +- SQLite via `sqlite3` stdlib or `sqlmodel` if an ORM is wanted — keep it + simple, this doesn't need SQLAlchemy's full weight +- `systemd` service file for deployment (pattern-match the existing + `notify.sh` / MakeMKV LXC setup — same conventions for logging and restarts) + +## Data Model + +- `recipes(id, name, servings, instructions, source, is_keto, is_nut_free)` +- `ingredients(id, recipe_id, name, quantity, unit, category)` +- `staples(id, name, default_quantity, unit, category)` +- `grocery_list_items(id, name, quantity, unit, category, source)` + - `source` is one of `staple`, `recipe`, `freeform` — keep provenance so + items can be intelligently merged/deduped and so `/clearlist` behavior + is predictable + +## Bot Commands (initial scope) + +- `/addrecipe` — conversational flow OR paste structured text/URL to parse +- `/recipes [tag]` — list/filter recipes +- `/staples` — show current staples list +- `/staples add <item>` / `/staples remove <item>` +- `/grocerylist [recipe1, recipe2, ...]` — staples + named recipes merged + into one list, quantities summed for matching items +- `/add <item>` — freeform one-off add to the current grocery list +- `/clearlist` — reset the working grocery list +- `/random [tag]` — random recipe suggestion, optionally filtered + +## Merge Logic Rules + +- Same-name ingredients across recipes/staples/freeform sum their quantities + when units match; if units don't match, keep as separate line items rather + than guessing a conversion. +- Category (produce, dairy, pantry, etc.) is used for grouping the final list + output, not for merge logic. + +## Code Style / Conventions + +- Match the conventions already used in Jake's other homelab projects: + small, readable functions over cleverness; explicit error handling; + Pushover notification on failures via the existing `notify.sh` pattern + where relevant (e.g. bot crash, DB write failure). +- No unnecessary abstraction layers — this is a personal tool, not a + library. Prefer a flat file structure (`bot.py`, `db.py`, `models.py`, + `parsing.py`) over deep package nesting. +- Config (Telegram bot token, DB path) via environment variables or a + `.env` file — never hardcoded, never committed. + +## Out of Scope (for now) + +- Web UI — SQLite backend should make this easy to bolt on later if wanted +- Multi-user support — this is a single-household tool +- Nutrition/macro calculation beyond keto/nut-free tagging + +## Testing + +- Unit tests for merge logic (the trickiest part) using `pytest` +- Manual testing against a test Telegram bot token before pointing at the + real one diff --git a/SPEC.md b/SPEC.md @@ -0,0 +1,81 @@ +# Recipe & Grocery Bot — Project Spec + +## Overview + +A Telegram bot, self-hosted on the homelab, for tracking recipes with +dietary tags (keto, nut-free), maintaining a weekly staples list, and +generating merged grocery lists on demand. + +## Why Telegram over Slack + +Telegram's Bot API supports long polling, so the bot can run entirely +inside the Tailscale network with no public-facing webhook, no reverse +proxy, and no OAuth app-review process. Slack bots generally want a public +HTTPS endpoint (or Socket Mode with more setup overhead) for the same +result. Telegram is the lower-friction choice for a single-user homelab +tool. + +## Setup Steps + +1. Create a bot via [@BotFather](https://t.me/BotFather) on Telegram, get + the bot token. +2. Deploy target: new LXC or VM on Proxmox (or a lightweight container), + consistent with existing homelab conventions. +3. `pip install python-telegram-bot sqlite-utils python-dotenv` +4. Store the bot token in a `.env` file, excluded from version control. +5. Initialize the SQLite DB from `models.py` on first run (auto-create + tables if they don't exist). +6. Register a `systemd` unit for the bot process — restart on failure, + logs to journald (or wherever the rest of the stack logs to). +7. Optional: wire a Pushover notification into `notify.sh` for bot crashes + or DB errors, matching the existing pattern. + +## Data Model + +See `CLAUDE.md` for full schema. Summary: + +| Table | Purpose | +|---|---| +| `recipes` | Name, servings, instructions, source, dietary flags | +| `ingredients` | Per-recipe ingredient lines | +| `staples` | Recurring weekly items with default quantities | +| `grocery_list_items` | Current working list, tagged by source (staple/recipe/freeform) | + +## Command Reference + +| Command | Behavior | +|---|---| +| `/addrecipe` | Add a new recipe, conversationally or by pasting text | +| `/recipes [tag]` | List recipes, optionally filtered by tag | +| `/staples` | View the weekly staples list | +| `/staples add <item>` | Add an item to staples | +| `/staples remove <item>` | Remove an item from staples | +| `/grocerylist [recipes...]` | Generate merged list: staples + named recipes | +| `/add <item>` | Freeform add to the current grocery list | +| `/clearlist` | Reset the working grocery list | +| `/random [tag]` | Suggest a random recipe, optionally filtered | + +## Dietary Safety Notes + +Nut-free is treated as an allergy constraint, not a preference — the bot +should never silently mark something nut-free if any ingredient is +ambiguous. Keto tagging can be looser (auto-suggested, manually +correctable). + +## Future Ideas (not in initial build) + +- Web UI reading from the same SQLite DB +- Recipe URL parsing/import +- Grocery list grouped by store section +- Meal-planning calendar view + +## Handoff to Claude Code + +Point Claude Code at this directory with both `CLAUDE.md` and `SPEC.md` +present. `CLAUDE.md` governs coding conventions and hard constraints; +`SPEC.md` is the human-readable reference. Suggested first prompt: + +> Build the initial project structure per SPEC.md: SQLite schema, the +> `/addrecipe`, `/recipes`, `/staples`, `/grocerylist`, `/add`, and +> `/clearlist` commands, and a systemd unit file. Start with the DB layer +> and merge logic, then wire up the bot commands.