Riscrittura in Python dell'assistente Avatar con interfaccia HUD (derivata da Mark LIV, CC BY-NC 4.0, vedi NOTICE.md). Tre motori (Claude API, server locale OpenAI-compatibile, Claude Code), voce Kokoro/macOS, Whisper MLX, avatar 3D con sincronizzazione labiale, memoria per categorie, allegati con OCR, monitor con avvisi, plugin per Calendario, Mail, Promemoria, Note, Musica, app, Mac, timer, meteo, contatti, Messaggi, file, Comandi Rapidi, browser, Telegram, WhatsApp (archivio e tempo reale). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
110 lines
4.8 KiB
Python
110 lines
4.8 KiB
Python
"""Timer, sveglie e attività programmate: l'app avvisa a voce o esegue un comando all'ora stabilita, anche ogni giorno."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import uuid
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
from avatar.settings import DATA_DIR # noqa: E402
|
|
|
|
FILE = DATA_DIR / "timers.json"
|
|
|
|
|
|
def load() -> list[dict]:
|
|
try:
|
|
return json.loads(FILE.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return []
|
|
|
|
|
|
def save(items: list[dict]) -> None:
|
|
FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
FILE.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
|
|
|
|
def _when(params: dict) -> datetime:
|
|
now = datetime.now()
|
|
if params.get("minuti"):
|
|
return now + timedelta(minutes=float(params["minuti"]))
|
|
s = str(params.get("ora", "")).strip().replace("T", " ")
|
|
if not s:
|
|
raise ValueError("Serve 'minuti' oppure 'ora'.")
|
|
if len(s) <= 5: # HH:MM oggi (o domani se già passata)
|
|
t = datetime.strptime(s, "%H:%M").time()
|
|
dt = datetime.combine(now.date(), t)
|
|
return dt if dt > now else dt + timedelta(days=1)
|
|
return datetime.strptime(s, "%Y-%m-%d %H:%M")
|
|
|
|
|
|
def imposta(params: dict, ctx: dict) -> str:
|
|
testo = str(params.get("testo", "")).strip() or "Timer scaduto"
|
|
try:
|
|
at = _when(params)
|
|
except ValueError as err:
|
|
return f"Errore: {err}"
|
|
item = {"id": uuid.uuid4().hex[:6], "at": at.strftime("%Y-%m-%d %H:%M"), "testo": testo,
|
|
"ripeti": "giorno" if params.get("ogni_giorno") else "no", "comando": bool(params.get("esegui_come_comando"))}
|
|
items = load(); items.append(item); save(items)
|
|
quando = at.strftime("%H:%M") if at.date() == datetime.now().date() else at.strftime("%d/%m alle %H:%M")
|
|
tipo = "comando" if item["comando"] else "avviso"
|
|
return f"{tipo.capitalize()} programmato per le {quando}{' ogni giorno' if item['ripeti'] == 'giorno' else ''}: «{testo}» [id {item['id']}]."
|
|
|
|
|
|
def elenca(params: dict, ctx: dict) -> str:
|
|
items = sorted(load(), key=lambda x: x["at"])
|
|
if not items:
|
|
return "Nessun timer o attività programmata."
|
|
return "Programmati:\n" + "\n".join(f"- [{i['id']}] {i['at']}{' ogni giorno' if i.get('ripeti') == 'giorno' else ''}: {i['testo']}{' (comando)' if i.get('comando') else ''}" for i in items)
|
|
|
|
|
|
def annulla(params: dict, ctx: dict) -> str:
|
|
key = str(params.get("id", "")).strip().lower()
|
|
items = load()
|
|
keep = [i for i in items if i["id"] != key and key not in i["testo"].lower()] if key else []
|
|
if key and len(keep) == len(items):
|
|
return "Nessun timer corrispondente."
|
|
if not key:
|
|
save([]); return "Tutti i timer annullati."
|
|
save(keep)
|
|
return f"Annullati {len(items) - len(keep)} timer."
|
|
|
|
|
|
def due(now: datetime | None = None) -> list[dict]:
|
|
"""Restituisce gli elementi scaduti e aggiorna il file (usato dallo scheduler dell'app)."""
|
|
now = now or datetime.now()
|
|
items, fired, keep = load(), [], []
|
|
for i in items:
|
|
try:
|
|
at = datetime.strptime(i["at"], "%Y-%m-%d %H:%M")
|
|
except ValueError:
|
|
continue
|
|
if at <= now:
|
|
fired.append(i)
|
|
if i.get("ripeti") == "giorno":
|
|
nxt = at + timedelta(days=1)
|
|
while nxt <= now:
|
|
nxt += timedelta(days=1)
|
|
keep.append({**i, "at": nxt.strftime("%Y-%m-%d %H:%M")})
|
|
else:
|
|
keep.append(i)
|
|
if fired:
|
|
save(keep)
|
|
return fired
|
|
|
|
|
|
TOOLS = [
|
|
{"name": "timer_imposta",
|
|
"description": "Imposta un timer ('tra 10 minuti'), una sveglia/avviso a un'ora, o un'attività programmata anche giornaliera. Con esegui_come_comando=true, all'ora stabilita il testo viene eseguito come richiesta all'assistente (es. 'leggimi la posta'), altrimenti viene annunciato a voce.",
|
|
"parameters": {"type": "object", "properties": {"testo": {"type": "string", "description": "Cosa annunciare, o il comando da eseguire."},
|
|
"minuti": {"type": "number", "description": "Fra quanti minuti."},
|
|
"ora": {"type": "string", "description": "HH:MM oppure AAAA-MM-GG HH:MM."},
|
|
"ogni_giorno": {"type": "boolean"}, "esegui_come_comando": {"type": "boolean"}}, "required": ["testo"]},
|
|
"run": imposta},
|
|
{"name": "timer_elenca", "description": "Elenca timer e attività programmate.", "parameters": {"type": "object", "properties": {}}, "run": elenca},
|
|
{"name": "timer_annulla", "description": "Annulla un timer per id o parola del testo; senza id annulla tutti.",
|
|
"parameters": {"type": "object", "properties": {"id": {"type": "string"}}}, "run": annulla},
|
|
]
|