Erinnerungen & Routinen (A3): Wecker in MC2 + Hermes-Tools

- services/reminders.py: persistenter Store (/srv/models/mc2-reminders.json),
  Wecker-Loop feuert in Briefkasten (Lucy spricht) + Telegram; repeat
  daily/weekdays/weekly; naive Zeiten = Commander-TZ (Europe/Berlin, Box ist UTC);
  verpasste Einmal-Erinnerungen feuern verspätet nach (ehrlich markiert)
- routers/reminders.py: GET/POST/DELETE /api/reminders
- mcp_mc.py: reminder_create/list/delete (Guards, ISO aus Sofort-Kontext)
- announce.notify_telegram als gemeinsamer Helper (sentry nutzt ihn jetzt)
- requirements: tzdata (zoneinfo-Fallback, Windows-Dev)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 14:02:30 +02:00
parent 019bd6d18b
commit 2b124d5b0a
7 changed files with 272 additions and 16 deletions
+17
View File
@@ -13,6 +13,8 @@ MAX_ITEMS Einträge, ein Lock für die FastAPI-Threadpool-Worker.
import json
import logging
import os
import shutil
import subprocess
import threading
import time
from pathlib import Path
@@ -21,6 +23,8 @@ from config import MODELS_DIR
log = logging.getLogger(__name__)
NOTIFY_SH = str(Path(__file__).resolve().parent.parent.parent / "deploy" / "notify.sh")
STORE_PATH = Path(os.environ.get("MC_ANNOUNCE_STORE", str(MODELS_DIR / "mc2-announce.json")))
MAX_ITEMS = int(os.environ.get("MC_ANNOUNCE_MAX", "200"))
@@ -72,6 +76,19 @@ def add(text: str, subject: str = "", source: str = "", priority: str = "normal"
return item
def notify_telegram(subject: str, text: str) -> None:
"""Best-effort auch auf Telegram (User ist evtl. nicht am PC). MC_NOTIFY_NO_ANNOUNCE=1
verhindert, dass notify.sh die Meldung ZURÜCK in den Briefkasten spiegelt — der Absender
(Wächter/Erinnerung) hat sie dort schon selbst abgelegt. Auf Windows (Dev) ein No-op."""
if not (os.name == "posix" and shutil.which("bash")):
return
try:
subprocess.run(["bash", NOTIFY_SH, "-s", subject, text],
timeout=30, capture_output=True, env={**os.environ, "MC_NOTIFY_NO_ANNOUNCE": "1"})
except Exception:
log.warning("notify_telegram: notify.sh fehlgeschlagen", exc_info=True)
def list_after(after: int | None, limit: int = 20) -> dict:
"""Einträge NACH Cursor `after` (aufsteigend). Ohne Cursor nur den aktuellen
Stand liefern (latest) — so initialisiert Lucy ihren Cursor, ohne Altes nachzuplappern."""