import os import sys from urllib.parse import urlparse import requests from datetime import datetime, timedelta, timezone if sys.stdout.encoding and sys.stdout.encoding.lower() != "utf-8": sys.stdout.reconfigure(encoding="utf-8") AW_BASE_URL = "http://localhost:5600/api/0" def get_recent_activity(hours=1): end_time = datetime.now(timezone.utc) start_time = end_time - timedelta(hours=hours) buckets = requests.get(f"{AW_BASE_URL}/buckets/").json() targets = ["aw-watcher-window", "aw-watcher-web-chrome", "aw-watcher-vscode"] bucket_ids = [b for b in buckets if any(t in b for t in targets)] if not bucket_ids: raise RuntimeError("No matching ActivityWatch buckets found.") params = {"start": start_time.isoformat(), "end": end_time.isoformat()} events = [] for bucket_id in bucket_ids: resp = requests.get( f"{AW_BASE_URL}/buckets/{bucket_id}/events", params=params, ) resp.raise_for_status() bucket_events = resp.json() if isinstance(bucket_events, dict): if "events" in bucket_events: bucket_events = bucket_events["events"] else: raise RuntimeError(f"Unexpected response: {bucket_events}") for e in bucket_events: e["_bucket"] = bucket_id events.append(e) def parse_ts(ts): if ts.endswith("Z"): ts = ts[:-1] + "+00:00" return datetime.fromisoformat(ts) def is_unknown(value): return value is None or value == "" or value == "unknown" def basename_any(path_value): if is_unknown(path_value): return "" normalized = str(path_value).replace("\\", "/") return os.path.basename(normalized.rstrip("/")) def strip_suffix(text, suffix): if text.endswith(suffix): return text[: -len(suffix)] return text def esc(value): return str(value).replace('"', '\\"') def format_event(e, tz): bucket = e.get("_bucket", "") data = e.get("data", {}) duration = e.get("duration", 0) if not duration or duration == 0: return None if "aw-watcher-web" in bucket: type_label = "web" parsed = urlparse(data.get("url") or "") domain = parsed.hostname or "unknown" name = f"Chrome.exe:{domain}" content = data.get("title") or data.get("url") or "" content = strip_suffix(content, " - Google Chrome") elif "aw-watcher-vscode" in bucket: type_label = "code" project_name = basename_any(data.get("project")) or "unknown" name = f"Code.exe:{project_name}" content = data.get("title") or data.get("file") or "" content = strip_suffix(content, " - Visual Studio Code") else: type_label = "window" app = data.get("app") or "unknown" name = app content = data.get("title") or "" content = strip_suffix(content, " - Google Chrome") if is_unknown(name) and is_unknown(content): return None timestamp = parse_ts(e["timestamp"]).astimezone(tz).strftime("%H:%M") return f'[{timestamp}] "{esc(type_label)}" "{esc(name)}" "{esc(content)}" "{duration}"' tz_local = timezone(timedelta(hours=8)) events.sort(key=lambda e: e["timestamp"]) summary = [] for e in events: line = format_event(e, tz_local) if line: summary.append(line) return "\n".join(summary) print(get_recent_activity())