"""Note di macOS: crea, cerca e legge note (AppleScript)."""
from __future__ import annotations
import html
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from avatar.macos import osa # noqa: E402
SEP = "␟"
def _testo(body_html: str) -> str:
t = re.sub(r"
||
|", "\n", body_html, flags=re.I)
t = html.unescape(re.sub(r"<[^>]+>", "", t))
return "\n".join(line.strip() for line in t.splitlines() if line.strip())
def crea(params: dict, ctx: dict) -> str:
titolo = str(params.get("titolo", "")).strip() or "Nota"
testo = str(params.get("testo", "")).strip()
body = f"{html.escape(titolo)}
" + "".join(f"{html.escape(l) or '
'}
" for l in testo.splitlines())
osa('on run argv\ntell application "Notes" to make new note at folder "Notes" with properties {name:item 1 of argv, body:item 2 of argv}\nend run', titolo, body)
return f"Nota creata: «{titolo}»."
def cerca(params: dict, ctx: dict) -> str:
q = str(params.get("query", "")).strip()
if not q:
return "Errore: serve il campo 'query'."
out = osa(f'''on run argv
set sep to "{SEP}"
set out to ""
set n to 0
tell application "Notes"
repeat with nt in (notes whose name contains (item 1 of argv) or plaintext contains (item 1 of argv))
set n to n + 1
if n > 8 then exit repeat
set out to out & (name of nt) & sep & ((modification date of nt) as string) & linefeed
end repeat
end tell
return out
end run''', q, timeout=90)
rows = [l.split(SEP) for l in out.splitlines() if l]
if not rows:
return f"Nessuna nota trovata per '{q}'."
return f"Note trovate per '{q}':\n" + "\n".join(f"- {r[0]} ({r[1]})" for r in rows) + "\nPer leggerne una usa note_leggi con il titolo."
def leggi(params: dict, ctx: dict) -> str:
titolo = str(params.get("titolo", "")).strip()
if not titolo:
return "Errore: serve il titolo."
out = osa('on run argv\ntell application "Notes" to return body of (first note whose name contains (item 1 of argv))\nend run', titolo, timeout=60)
testo = _testo(out)
return testo[:2500] + (" […]" if len(testo) > 2500 else "") if testo else "Nota vuota."
def aggiungi(params: dict, ctx: dict) -> str:
titolo = str(params.get("titolo", "")).strip()
testo = str(params.get("testo", "")).strip()
if not titolo or not testo:
return "Errore: servono titolo (della nota esistente) e testo da aggiungere."
extra = "".join(f"{html.escape(l) or '
'}
" for l in testo.splitlines())
osa('on run argv\ntell application "Notes"\nset nt to first note whose name contains (item 1 of argv)\nset body of nt to (body of nt) & (item 2 of argv)\nend tell\nend run', titolo, extra)
return f"Aggiunto alla nota «{titolo}»."
TOOLS = [
{"name": "note_crea", "description": "Crea una nuova nota nell'app Note con titolo e testo. Usalo per 'prendi nota', 'appunta che…'.",
"parameters": {"type": "object", "properties": {"titolo": {"type": "string"}, "testo": {"type": "string"}}, "required": ["testo"]}, "run": crea},
{"name": "note_aggiungi", "description": "Aggiunge testo in fondo a una nota esistente, cercata per titolo.",
"parameters": {"type": "object", "properties": {"titolo": {"type": "string"}, "testo": {"type": "string"}}, "required": ["titolo", "testo"]}, "run": aggiungi},
{"name": "note_cerca", "description": "Cerca note per parola nel titolo o nel testo.",
"parameters": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}, "run": cerca},
{"name": "note_leggi", "description": "Legge il contenuto di una nota cercata per titolo.",
"parameters": {"type": "object", "properties": {"titolo": {"type": "string"}}, "required": ["titolo"]}, "run": leggi},
]