Files
aza/AzA march 2026/aza_empfang_shell_surface.py
2026-06-13 22:47:31 +02:00

190 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
"""IPC-Datei: aktiver Chat-Peer der Empfang-WebView (keine Nachrichteninhalte)."""
from __future__ import annotations
import json
import time
from pathlib import Path
from typing import Any, Optional
_SURFACE_TTL_SEC = 45.0
def shell_surface_state_path() -> Path:
try:
from aza_persistence import get_writable_data_dir
root = Path(get_writable_data_dir())
except Exception:
root = Path.home() / ".aza"
return root / "empfang_shell_surface.json"
def write_shell_surface_state(payload: dict[str, Any]) -> None:
data = {
"ts": time.time(),
"mode": str(payload.get("mode") or "").strip()[:32],
"peer_user_id": str(payload.get("peer_user_id") or "").strip()[:64],
"external_peer_user_id": str(payload.get("external_peer_user_id") or "").strip()[:64],
"document_visible": bool(payload.get("document_visible")),
"has_focus": bool(payload.get("has_focus")),
"shell_minimized": bool(payload.get("shell_minimized")),
}
path = shell_surface_state_path()
try:
path.parent.mkdir(parents=True, exist_ok=True)
tmp = path.with_suffix(".tmp")
tmp.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
tmp.replace(path)
except Exception:
pass
def read_shell_surface_state(*, max_age_sec: float = _SURFACE_TTL_SEC) -> Optional[dict[str, Any]]:
path = shell_surface_state_path()
try:
if not path.is_file():
return None
raw = path.read_text(encoding="utf-8")
data = json.loads(raw)
if not isinstance(data, dict):
return None
age = time.time() - float(data.get("ts") or 0)
if age > float(max_age_sec):
return None
return data
except Exception:
return None
def peer_matches_surface(peer_user_id: str, surface: Optional[dict[str, Any]]) -> bool:
puid = (peer_user_id or "").strip()
if not puid or not isinstance(surface, dict):
return False
for k in ("peer_user_id", "external_peer_user_id"):
if str(surface.get(k) or "").strip() == puid:
return True
return False
def surface_indicates_active_direct_chat(
peer_user_id: str,
*,
require_visible: bool = True,
require_not_minimized: bool = True,
) -> bool:
st = read_shell_surface_state()
if not st:
return False
if str(st.get("mode") or "") != "direct":
return False
if require_visible and not bool(st.get("document_visible")):
return False
if require_not_minimized and bool(st.get("shell_minimized")):
return False
return peer_matches_surface(peer_user_id, st)
def shell_peer_refresh_signal_path() -> Path:
try:
from aza_persistence import get_writable_data_dir
root = Path(get_writable_data_dir())
except Exception:
root = Path.home() / ".aza"
return root / "empfang_shell_peer_refresh.json"
def touch_shell_peer_refresh_signal(*, source: str = "") -> None:
"""Kontakt-Panel / Fokus-Pfad: Huelle soll Shell-Context erneut lesen (keine Peer-IDs)."""
path = shell_peer_refresh_signal_path()
try:
path.parent.mkdir(parents=True, exist_ok=True)
tmp = path.with_suffix(".tmp")
tmp.write_text(
json.dumps(
{"ts": time.time(), "source": str(source or "").strip()[:32]},
ensure_ascii=False,
),
encoding="utf-8",
)
tmp.replace(path)
except Exception:
pass
def consume_shell_peer_refresh_signal(last_seen_ts: float = 0.0) -> float:
"""Liefert neuen Signal-Zeitstempel oder 0.0 — ohne Inhalte zu loggen."""
path = shell_peer_refresh_signal_path()
try:
if not path.is_file():
return 0.0
data = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
return 0.0
ts = float(data.get("ts") or 0.0)
if ts <= float(last_seen_ts or 0.0):
return 0.0
return ts
except Exception:
return 0.0
def empfang_shell_debug_log_path() -> Path:
try:
from aza_persistence import get_writable_data_dir
root = Path(get_writable_data_dir())
except Exception:
root = Path.home() / ".aza"
return root / "empfang_shell_debug.log"
def append_empfang_shell_debug_log(line: str) -> None:
"""Technische Kurzzeile ohne Tokens, Peer-IDs oder URLs mit Query."""
msg = str(line or "").strip()
if not msg:
return
path = empfang_shell_debug_log_path()
try:
path.parent.mkdir(parents=True, exist_ok=True)
stamp = time.strftime("%Y-%m-%dT%H:%M:%S")
with path.open("a", encoding="utf-8", newline="\n") as f:
f.write(f"{stamp} {msg}\n")
except Exception:
pass
_OFFICE_EMPFANG_CHAT_SHELL_OPEN_REQUEST = "office_empfang_chat_shell_open_request.flag"
def _office_shell_ipc_path(name: str) -> Path:
try:
from aza_persistence import get_writable_data_dir
root = Path(get_writable_data_dir())
except Exception:
root = Path.home() / ".aza"
return root / name
def request_office_open_empfang_chat_shell_ipc() -> None:
"""Kontakt-Panel -> Office: Chat-Huelle per Office-Starter oeffnen/fokussieren."""
try:
path = _office_shell_ipc_path(_OFFICE_EMPFANG_CHAT_SHELL_OPEN_REQUEST)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("1", encoding="utf-8")
except Exception:
pass
def consume_office_open_empfang_chat_shell_request_flag() -> bool:
try:
path = _office_shell_ipc_path(_OFFICE_EMPFANG_CHAT_SHELL_OPEN_REQUEST)
if not path.is_file():
return False
path.unlink(missing_ok=True)
return True
except Exception:
return False