mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-10 19:37:03 +01:00
Two commands, food_today and workout_today, each printing the current Warsaw day out of a log another program owns: every meal with its time, calories and source plus the day's total and macros; every workout with every field it carries. Three workout fields are withheld (a reservation phone number, a screenshot path, the sync record id), and the food budget is never printed -- diet_guard keeps that number on the machine on purpose. The spellings the group uses carry a surname, so they are custom aliases in the state directory; aliases.json ships only plain words. The suite points both logs at absent temp files so it can never read the operator's own, and the e2e run writes one invented entry of each under today's date. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
2.3 KiB
Python
58 lines
2.3 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence.
|
|
"""One day out of a day-keyed JSON log another program keeps.
|
|
|
|
diet_guard and screen_locker both write the same shape: an object whose keys
|
|
are ``YYYY-MM-DD`` and whose values are lists of entry objects. Neither file
|
|
belongs to this bot -- they are read where they are, never copied and never
|
|
written -- so where they are is the operator's to say, through one
|
|
environment variable per log, with the path the two programs use by default.
|
|
|
|
The entries are HMAC-signed by their owners. This module does not check the
|
|
signatures, because the keys are those programs' secrets: what it reads is
|
|
what it shows, and a forged line in the operator's own home directory is a
|
|
problem the bot is not the place to solve.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
class DayLogError(RuntimeError):
|
|
"""The log cannot be read at all, as opposed to having nothing for a day."""
|
|
|
|
|
|
def path_from_env(variable: str, default: str) -> Path:
|
|
"""Where the log is: the variable if it is set, the program's default otherwise."""
|
|
explicit = os.environ.get(variable, "").strip()
|
|
return Path(explicit).expanduser() if explicit else Path(default).expanduser()
|
|
|
|
|
|
def read_day(path: Path, day: str) -> list[dict[str, Any]]:
|
|
"""Every entry the log holds for ``day``, in the order the file gives them.
|
|
|
|
A day the log has no key for is an empty list, not an error: an empty
|
|
day is the normal case before breakfast.
|
|
|
|
Raises:
|
|
DayLogError: The file is missing, unreadable, not JSON, or not the
|
|
object-of-days shape -- anything that makes "nothing today"
|
|
a claim this module cannot make.
|
|
"""
|
|
try:
|
|
document = json.loads(path.read_text(encoding="utf-8"))
|
|
except (OSError, json.JSONDecodeError) as error:
|
|
msg = f"{path} cannot be read: {error}"
|
|
raise DayLogError(msg) from error
|
|
if not isinstance(document, dict):
|
|
msg = f"{path} is not an object of days"
|
|
raise DayLogError(msg)
|
|
entries = document.get(day, [])
|
|
if not isinstance(entries, list):
|
|
msg = f"{path}: {day} is not a list of entries"
|
|
raise DayLogError(msg)
|
|
return [entry for entry in entries if isinstance(entry, dict)]
|